Merge remote-tracking branch 'origin/master'

This commit is contained in:
Maxim.Medvedev
2012-01-26 15:48:00 +04:00
32 changed files with 1205 additions and 136 deletions
@@ -62,7 +62,9 @@ import org.jetbrains.jps.server.Server;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.RunnableFuture;
@@ -73,8 +75,10 @@ import java.util.concurrent.TimeUnit;
* Date: 9/6/11
*/
public class CompileServerManager implements ApplicationComponent{
private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.JpsServerManager");
private static final Logger LOG = Logger.getInstance("#com.intellij.compiler.CompileServerManager");
private static final String COMPILE_SERVER_SYSTEM_ROOT = "compile-server";
private static final String LOGGER_CONFIG = "log.xml";
private static final String DEFAULT_LOGGER_CONFIG = "defaultLogConfig.xml";
private volatile OSProcessHandler myProcessHandler;
private final File mySystemDirectory;
private volatile CompileServerClient myClient = new CompileServerClient();
@@ -272,7 +276,7 @@ public class CompileServerManager implements ApplicationComponent{
return true;
}
};
final Ref<String> serverStartMessage = new Ref<String>(null);
final StringBuilder serverStartMessage = new StringBuilder();
final Semaphore semaphore = new Semaphore();
semaphore.down();
processHandler.addProcessListener(new ProcessAdapter() {
@@ -298,8 +302,11 @@ public class CompileServerManager implements ApplicationComponent{
if (text != null) {
if (text.contains(Server.SERVER_SUCCESS_START_MESSAGE) || text.contains(Server.SERVER_ERROR_START_MESSAGE)) {
processHandler.removeProcessListener(this);
serverStartMessage.set(text);
}
if (serverStartMessage.length() > 0) {
serverStartMessage.append("\n");
}
serverStartMessage.append(text);
}
}
finally {
@@ -311,8 +318,8 @@ public class CompileServerManager implements ApplicationComponent{
processHandler.startNotify();
semaphore.waitFor();
final String startupMsg = serverStartMessage.get();
if (startupMsg == null || !startupMsg.contains(Server.SERVER_SUCCESS_START_MESSAGE)) {
final String startupMsg = serverStartMessage.toString();
if (!startupMsg.contains(Server.SERVER_SUCCESS_START_MESSAGE)) {
throw new Exception("Server startup failed: " + startupMsg);
}
@@ -462,14 +469,43 @@ public class CompileServerManager implements ApplicationComponent{
final File workDirectory = new File(mySystemDirectory, COMPILE_SERVER_SYSTEM_ROOT);
workDirectory.mkdirs();
ensureLogConfigExists(workDirectory);
cmdLine.addParameter(FileUtil.toSystemIndependentName(workDirectory.getPath()));
cmdLine.setWorkDirectory(workDirectory);
return cmdLine.createProcess();
}
private static void ensureLogConfigExists(File workDirectory) {
final File logConfig = new File(workDirectory, LOGGER_CONFIG);
if (!logConfig.exists()) {
FileUtil.createIfDoesntExist(logConfig);
try {
final InputStream in = Server.class.getResourceAsStream("/" + DEFAULT_LOGGER_CONFIG);
if (in != null) {
try {
final FileOutputStream out = new FileOutputStream(logConfig);
try {
FileUtil.copy(in, out);
}
finally {
out.close();
}
}
finally {
in.close();
}
}
}
catch (IOException e) {
LOG.error(e);
}
}
}
public void shutdownServer() {
shutdownServer(myClient, myProcessHandler);
}
@@ -140,6 +140,7 @@ public class JavaExecutionUtil {
@Nullable
public static PsiClass findMainClass(final Project project, final String mainClassName, final GlobalSearchScope scope) {
if (project.isDefault()) return null;
final PsiManager psiManager = PsiManager.getInstance(project);
final String shortName = StringUtil.getShortName(mainClassName);
final String packageName = StringUtil.getPackageName(mainClassName);
@@ -16,14 +16,16 @@
package com.intellij.psi.impl.file;
import com.intellij.ide.projectView.ProjectView;
import com.intellij.ide.projectView.impl.PackageViewPane;
import com.intellij.ide.projectView.impl.ProjectRootsUtil;
import com.intellij.ide.projectView.impl.nodes.PackageElement;
import com.intellij.ide.projectView.impl.ProjectViewPane;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.undo.GlobalUndoableAction;
import com.intellij.openapi.command.undo.UndoManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.*;
import com.intellij.openapi.vfs.VfsUtil;
@@ -31,9 +33,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.psi.NonClasspathClassFinder;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.*;
import com.intellij.psi.impl.PackagePrefixElementFinder;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiModificationTracker;
@@ -137,18 +137,36 @@ public class PsiPackageImplementationHelperImpl extends PsiPackageImplementation
@Override
public void run() {
final ProjectView projectView = ProjectView.getInstance(project);
projectView.changeView(PackageViewPane.ID);
final PsiDirectory[] directories = psiPackage.getDirectories();
final VirtualFile firstDir = directories[0].getVirtualFile();
final boolean isLibraryRoot = ProjectRootsUtil.isLibraryRoot(firstDir, project);
final Module module = ProjectRootManager.getInstance(project).getFileIndex().getModuleForFile(firstDir);
final PackageElement packageElement = new PackageElement(module, psiPackage, isLibraryRoot);
projectView.getProjectViewPaneById(PackageViewPane.ID).select(packageElement, firstDir, requestFocus);
PsiDirectory[] directories = suggestMostAppropriateDirectories(psiPackage);
if (directories.length == 0) return;
projectView.getProjectViewPaneById(ProjectViewPane.ID).select(directories[0], directories[0].getVirtualFile(), requestFocus);
}
});
}
private static PsiDirectory[] suggestMostAppropriateDirectories(PsiPackage psiPackage) {
final Project project = psiPackage.getProject();
PsiDirectory[] directories = null;
final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
if (editor != null) {
final Document document = editor.getDocument();
final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
if (psiFile != null) {
final Module module = ModuleUtil.findModuleForPsiElement(psiFile);
if (module != null) {
directories = psiPackage.getDirectories(GlobalSearchScope.moduleWithDependenciesScope(module));
} else {
directories = psiPackage.getDirectories(GlobalSearchScope.notScope(GlobalSearchScope.projectScope(project)));
}
}
}
if (directories == null || directories.length == 0) {
directories = psiPackage.getDirectories();
}
return directories;
}
@Override
public boolean packagePrefixExists(PsiPackage psiPackage) {
return PackagePrefixElementFinder.getInstance(psiPackage.getProject()).packagePrefixExists(psiPackage.getQualifiedName());
@@ -131,11 +131,13 @@ public class ClassFilterEditor extends JPanel implements ComponentWithEmptyText
}
});
myAddPatternButton.setEnabled(!myProject.isDefault());
myAddPatternButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addPatternFilter();
}
});
myAddClassButton.setEnabled(!myProject.isDefault());
myAddClassButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addClassFilter();
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.err"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %c{1} - %m%n"/>
</layout>
</appender>
<root>
<priority value ="info" />
<appender-ref ref="console" />
</root>
</log4j:configuration>
@@ -34,6 +34,7 @@ public class Server {
private static final int MAX_SIMULTANEOUS_BUILD_SESSIONS = Math.max(2, Runtime.getRuntime().availableProcessors());
public static final String SERVER_SUCCESS_START_MESSAGE = "Compile Server started successfully. Listening on port: ";
public static final String SERVER_ERROR_START_MESSAGE = "Error starting Compile Server: ";
private static final String LOG_FILE_NAME = "log.xml";
private final ChannelGroup myAllOpenChannels = new DefaultChannelGroup("compile-server");
private final ChannelFactory myChannelFactory;
@@ -98,70 +99,14 @@ public class Server {
}
final Server server = new Server(systemDir);
DOMConfigurator.configure("log.xml");
Logger.setFactory(new Logger.Factory() {
@Override
public Logger getLoggerInstance(String category) {
final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(category);
return new Logger(){
@Override
public boolean isDebugEnabled() {
return logger.isDebugEnabled();
}
@Override
public void debug(@NonNls String message) {
logger.debug(message);
}
@Override
public void debug(@Nullable Throwable t) {
logger.debug("", t);
}
@Override
public void debug(@NonNls String message, @Nullable Throwable t) {
logger.debug(message, t);
}
@Override
public void error(@NonNls String message, @Nullable Throwable t, @NonNls String... details) {
logger.debug(message, t);
}
@Override
public void info(@NonNls String message) {
logger.info(message);
}
@Override
public void info(@NonNls String message, @Nullable Throwable t) {
logger.info(message, t);
}
@Override
public void warn(@NonNls String message, @Nullable Throwable t) {
logger.warn(message, t);
}
@Override
public void setLevel(Level level) {
logger.setLevel(level);
}
};
}
});
server.start(port);
Runtime.getRuntime().addShutdownHook(new Thread("Shutdown hook thread") {
public void run() {
server.stop();
}
});
initLoggers();
server.start(port);
ServerState.getInstance().setKeepTempCachesInMemory(System.getProperty(GlobalOptions.USE_MEMORY_TEMP_CACHE_OPTION) != null);
System.out.println("Server classpath: " + System.getProperty("java.class.path"));
@@ -174,6 +119,66 @@ public class Server {
}
}
private static void initLoggers() {
if (new File(LOG_FILE_NAME).exists()) {
DOMConfigurator.configure(LOG_FILE_NAME);
}
Logger.setFactory(new Logger.Factory() {
@Override
public Logger getLoggerInstance(String category) {
final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(category);
return new Logger() {
@Override
public boolean isDebugEnabled() {
return logger.isDebugEnabled();
}
@Override
public void debug(@NonNls String message) {
logger.debug(message);
}
@Override
public void debug(@Nullable Throwable t) {
logger.debug("", t);
}
@Override
public void debug(@NonNls String message, @Nullable Throwable t) {
logger.debug(message, t);
}
@Override
public void error(@NonNls String message, @Nullable Throwable t, @NonNls String... details) {
logger.debug(message, t);
}
@Override
public void info(@NonNls String message) {
logger.info(message);
}
@Override
public void info(@NonNls String message, @Nullable Throwable t) {
logger.info(message, t);
}
@Override
public void warn(@NonNls String message, @Nullable Throwable t) {
logger.warn(message, t);
}
@Override
public void setLevel(Level level) {
logger.setLevel(level);
}
};
}
});
}
private class ChannelRegistrar extends SimpleChannelUpstreamHandler {
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
myAllOpenChannels.add(e.getChannel());
@@ -112,6 +112,7 @@ public class AutoPopupController implements Disposable {
Runnable request = new Runnable() {
@Override
public void run() {
if (myProject.isDefault()) return;
CompletionAutoPopupHandler.runLaterWithCommitted(myProject, editor.getDocument(), new Runnable() {
@Override
public void run() {
@@ -15,7 +15,6 @@
*/
package com.intellij.ide.actions;
import com.intellij.idea.ActionsBundle;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
@@ -26,8 +25,6 @@ import com.intellij.openapi.fileEditor.TextEditor;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.Nullable;
public abstract class UndoRedoAction extends DumbAwareAction {
public UndoRedoAction() {
@@ -56,7 +53,7 @@ public abstract class UndoRedoAction extends DumbAwareAction {
}
UndoManager undoManager = getUndoManager(editor, dataContext);
presentation.setEnabled(isAvailable(editor, undoManager));
presentation.setEnabled(undoManager != null && isAvailable(editor, undoManager));
Pair<String, String> pair = getActionNameAndDescription(editor, undoManager);
@@ -50,6 +50,7 @@ import com.intellij.openapi.project.impl.ProjectManagerImpl;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.wm.IdeFrame;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.openapi.wm.ex.ProgressIndicatorEx;
@@ -78,6 +79,7 @@ import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@SuppressWarnings({"AssignmentToStaticFieldFromInstanceMethod"})
public class ApplicationImpl extends ComponentManagerImpl implements ApplicationEx {
@@ -117,6 +119,8 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application
private final AtomicBoolean mySaveSettingsIsInProgress = new AtomicBoolean(false);
@SuppressWarnings({"UseOfArchaicSystemPropertyAccessors"})
private static final int ourDumpThreadsOnLongWriteActionWaiting = Integer.getInteger("dump.threads.on.long.write.action.waiting", 0);
private final AtomicInteger myAliveThreads = new AtomicInteger(0);
private static final int ourReasonableThreadPoolSize = Registry.intValue("core.pooled.threads");
private final ExecutorService ourThreadExecutorsService = new ThreadPoolExecutor(
3,
@@ -127,6 +131,7 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application
new ThreadFactory() {
int i;
public Thread newThread(Runnable r) {
final int count = myAliveThreads.incrementAndGet();
final Thread thread = new Thread(r, "ApplicationImpl pooled thread "+i++) {
public void interrupt() {
if (LOG.isDebugEnabled()) {
@@ -144,9 +149,10 @@ public class ApplicationImpl extends ComponentManagerImpl implements Application
LOG.debug("Worker exits due to exception", t);
}
}
myAliveThreads.decrementAndGet();
}
};
if (ApplicationInfoImpl.getShadowInstance().isEAP() && i > 10) {
if (ApplicationInfoImpl.getShadowInstance().isEAP() && count > ourReasonableThreadPoolSize) {
LOG.info("Not enough pooled threads; creating one at:", new Throwable());
}
thread.setPriority(Thread.NORM_PRIORITY - 1);
@@ -181,6 +181,7 @@ dump.threads.on.empty.lookup.description=Whether IDEA should issue a thread dump
file.structure.tree.mode=true
python.new.style.resolve=false
disable.toolwindow.overlayed=true
disable.toolwindow.overlayed.description=Disable transparent toolwindow stripes
disable.toolwindow.overlayed.description=Disable transparent toolwindow stripes
core.pooled.threads=20
+12 -4
View File
@@ -128,10 +128,18 @@
<projectService
serviceInterface="git4idea.DialogManager"
serviceImplementation="git4idea.DialogManager"/>
<projectService serviceInterface="git4idea.history.wholeTree.GitLogSettings"
serviceImplementation="git4idea.history.wholeTree.GitLogSettings"/>
<applicationService serviceImplementation="git4idea.history.wholeTree.GitCommitsSequentialIndex"
serviceInterface="git4idea.history.wholeTree.GitCommitsSequentially"/>
<projectService
serviceInterface="git4idea.MessageManager"
serviceImplementation="git4idea.MessageManager"/>
<projectService
serviceInterface="git4idea.NotificationManager"
serviceImplementation="git4idea.NotificationManager"/>
<projectService
serviceInterface="git4idea.history.wholeTree.GitLogSettings"
serviceImplementation="git4idea.history.wholeTree.GitLogSettings"/>
<applicationService
serviceImplementation="git4idea.history.wholeTree.GitCommitsSequentialIndex"
serviceInterface="git4idea.history.wholeTree.GitCommitsSequentially"/>
<applicationService
serviceInterface="git4idea.config.GitVcsApplicationSettings"
serviceImplementation="git4idea.config.GitVcsApplicationSettings"/>
@@ -0,0 +1,43 @@
/*
* 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;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author Kirill Likhodedov
*/
public class MessageManager {
public static MessageManager getInstance(@NotNull Project project) {
return ServiceManager.getService(project, MessageManager.class);
}
public static int showYesNoDialog(Project project, String description, String title, String yesText, String noText, @Nullable Icon icon) {
return getInstance(project).doShowYesNoDialog(project, description, title, yesText, noText, icon);
}
@SuppressWarnings("MethodMayBeStatic")
protected int doShowYesNoDialog(Project project, String description, String title, String yesText, String noText, @Nullable Icon icon) {
return Messages.showYesNoDialog(project, description, title, yesText, noText, icon);
}
}
@@ -0,0 +1,56 @@
/*
* 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;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.NotificationListener;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Kirill Likhodedov
*/
public class NotificationManager {
@NotNull private final Project myProject;
public NotificationManager(@NotNull Project project) {
myProject = project;
}
public void notify(@NotNull NotificationGroup notificationGroup, @NotNull String title, @NotNull String message,
@NotNull NotificationType type, @Nullable NotificationListener listener) {
createNotification(notificationGroup, title, message, type, listener).notify(myProject);
}
public static NotificationManager getInstance(@NotNull Project project) {
return ServiceManager.getService(project, NotificationManager.class);
}
public void notify(@NotNull NotificationGroup notificationGroup, @NotNull String title, @NotNull String message, @NotNull NotificationType type) {
notify(notificationGroup, title, message, type, null);
}
protected static Notification createNotification(NotificationGroup notificationGroup, String title, String message,
NotificationType type, NotificationListener listener) {
return notificationGroup.createNotification(title, message, type, listener);
}
}
@@ -21,6 +21,7 @@ import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.ArrayUtil;
import git4idea.DialogManager;
import git4idea.GitBranch;
import git4idea.history.browser.GitCommit;
import git4idea.i18n.GitBundle;
@@ -76,7 +77,7 @@ public class GitBranchIsNotFullyMergedDialog extends DialogWrapper {
@NotNull List<String> mergedToBranches,
@Nullable String currentBranch) {
GitBranchIsNotFullyMergedDialog dialog = new GitBranchIsNotFullyMergedDialog(project, commits, branchToDelete, currentBranch, mergedToBranches);
dialog.show();
DialogManager.getInstance(project).showDialog(dialog);
return dialog.isOK();
}
@@ -24,10 +24,12 @@ import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.util.Function;
import com.intellij.util.ui.UIUtil;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.MessageManager;
import git4idea.NotificationManager;
import git4idea.merge.GitConflictResolver;
import git4idea.repo.GitRepository;
import git4idea.GitUtil;
import org.jetbrains.annotations.NotNull;
import javax.swing.event.HyperlinkEvent;
@@ -43,7 +45,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
abstract class GitBranchOperation {
private static final String UNMERGED_FILES_ERROR_TITLE = "Can't checkout because of unmerged files";
static final String UNMERGED_FILES_ERROR_TITLE = "Can't checkout because of unmerged files";
static final String UNMERGED_FILES_ERROR_NOTIFICATION_DESCRIPTION =
"You have to <a href='resolve'>resolve</a> all merge conflicts before checkout.<br/>" +
"After resolving conflicts you also probably would want to commit your files to the current branch.";
@NotNull protected final Project myProject;
@NotNull private final Collection<GitRepository> myRepositories;
@@ -129,7 +134,7 @@ abstract class GitBranchOperation {
}
protected void notifySuccess() {
GitVcs.NOTIFICATION_GROUP_ID.createNotification(getSuccessMessage(), NotificationType.INFORMATION).notify(myProject);
NotificationManager.getInstance(myProject).notify(GitVcs.NOTIFICATION_GROUP_ID, "", getSuccessMessage(), NotificationType.INFORMATION);
}
/**
@@ -151,7 +156,7 @@ abstract class GitBranchOperation {
public void run() {
String description = message + getRollbackProposal();
ok.set(Messages.OK ==
Messages.showYesNoDialog(myProject, description, title, "Rollback", "Don't rollback", Messages.getErrorIcon()));
MessageManager.showYesNoDialog(myProject, description, title, "Rollback", "Don't rollback", Messages.getErrorIcon()));
}
});
if (ok.get()) {
@@ -164,7 +169,7 @@ abstract class GitBranchOperation {
}
protected void notifyError(@NotNull String title, @NotNull String message) {
GitVcs.IMPORTANT_ERROR_NOTIFICATION.createNotification(title, message, NotificationType.ERROR, null).notify(myProject);
NotificationManager.getInstance(myProject).notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, title, message, NotificationType.ERROR);
}
@NotNull
@@ -192,8 +197,7 @@ abstract class GitBranchOperation {
String description = "You have to resolve all merge conflicts before checkout.<br/>" + getRollbackProposal();
// suppressing: this message looks ugly if capitalized by words
//noinspection DialogTitleCapitalization
ok.set(Messages.OK == Messages.showYesNoDialog(myProject, description, UNMERGED_FILES_ERROR_TITLE, "Rollback", "Don't rollback",
Messages.getErrorIcon()));
ok.set(Messages.OK == MessageManager.showYesNoDialog(myProject, description, UNMERGED_FILES_ERROR_TITLE, "Rollback", "Don't rollback", Messages.getErrorIcon()));
}
});
if (ok.get()) {
@@ -203,9 +207,8 @@ abstract class GitBranchOperation {
private void showUnmergedFilesNotification() {
String title = UNMERGED_FILES_ERROR_TITLE;
String description = "You have to <a href='resolve'>resolve</a> all merge conflicts before checkout.<br/>" +
"After resolving conflicts you also probably would want to commit your files to the current branch.";
GitVcs.IMPORTANT_ERROR_NOTIFICATION.createNotification(title, description, NotificationType.ERROR, new NotificationListener() {
String description = UNMERGED_FILES_ERROR_NOTIFICATION_DESCRIPTION;
NotificationManager.getInstance(myProject).notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, title, description, NotificationType.ERROR, new NotificationListener() {
@Override public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED && event.getDescription().equals("resolve")) {
GitConflictResolver.Params params = new GitConflictResolver.Params().
@@ -214,7 +217,7 @@ abstract class GitBranchOperation {
new GitConflictResolver(myProject, GitUtil.getRoots(getRepositories()), params).merge();
}
}
}).notify(myProject);
});
}
}
@@ -23,14 +23,12 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.ui.UIUtil;
import git4idea.GitExecutionException;
import git4idea.GitVcs;
import git4idea.commands.Git;
import git4idea.history.GitHistoryUtils;
import git4idea.history.browser.GitCommit;
import git4idea.merge.GitConflictResolver;
import git4idea.repo.GitRepository;
import git4idea.ui.branch.GitBranchUiUtil;
import git4idea.ui.branch.GitCompareBranchesDialog;
@@ -125,15 +123,6 @@ public final class GitBranchOperationsProcessor {
new GitCheckoutNewBranchOperation(myProject, myRepositories, name, getCurrentBranch(), indicator).execute();
}
@NotNull
static GitConflictResolver prepareConflictResolverForUnmergedFilesBeforeCheckout(Project project, Collection<VirtualFile> roots) {
GitConflictResolver.Params params = new GitConflictResolver.Params().
setMergeDescription("The following files have unresolved conflicts. You need to resolve them before checking out.").
setErrorNotificationTitle("Can't create new branch");
return new GitConflictResolver(project, roots, params);
}
/**
* Creates and checks out a new local branch starting from the given reference:
* {@code git checkout -b <branchname> <start-point>}. <br/>
@@ -38,6 +38,7 @@ import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.text.DateFormatUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcsUtil.VcsUtil;
import git4idea.DialogManager;
import git4idea.GitUtil;
import git4idea.GitVcs;
import git4idea.commands.*;
@@ -206,7 +207,7 @@ public class GitCheckoutOperation extends GitBranchOperation {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
dialog.show();
DialogManager.getInstance(myProject).showDialog(dialog);
}
});
@@ -274,7 +275,7 @@ public class GitCheckoutOperation extends GitBranchOperation {
catch (VcsException e) {
// ignoring the exception: this is not fatal if we won't collect such a diff from other repositories.
// At worst, use will get double dialog proposing the smart checkout.
LOG.warn(String.format("Couldn't collect diff between %s and %s in %s", myPreviousBranch, myStartPointReference, repository.getRoot()));
LOG.warn(String.format("Couldn't collect diff between %s and %s in %s", myPreviousBranch, myStartPointReference, repository.getRoot()), e);
}
}
return changes;
@@ -22,6 +22,7 @@ import com.intellij.openapi.vcs.changes.ui.ChangesBrowser;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.ui.UIUtil;
import git4idea.DialogManager;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -49,7 +50,7 @@ class GitWouldBeOverwrittenByCheckoutDialog extends DialogWrapper {
@Override
public void run() {
GitWouldBeOverwrittenByCheckoutDialog dialog = new GitWouldBeOverwrittenByCheckoutDialog(project, changes);
dialog.show();
DialogManager.getInstance(project).showDialog(dialog);
ok.set(dialog.isOK());
}
});
@@ -493,7 +493,7 @@ executable.error.title = Git executable problem
executable.error.description = Git couldn't be started. Probably the path to Git executable is not valid. <a href="">Fix it.</a>
git.commit.message.empty=Please specify commit message
git.commit.message.empty.title=Commit message is empty
git.commit.message.empty.title=Commit Message Is Empty
git.executable.notification.title=Can't start Git
git.executable.notification.description=Probably the path to Git executable is not valid. <a href=''>Fix it.</a>
@@ -201,7 +201,7 @@ public final class GitRepository implements Disposable {
* Returns the full-functional instance of GitRepository - with UntrackedFilesHolder and GitRepositoryUpdater.
* This is used for repositories registered in project, and should be optained via {@link GitRepositoryManager}.
*/
static GitRepository getFullInstance(@NotNull VirtualFile root, @NotNull Project project, @NotNull Disposable parentDisposable) {
public static GitRepository getFullInstance(@NotNull VirtualFile root, @NotNull Project project, @NotNull Disposable parentDisposable) {
GitRepository repository = new GitRepository(root, project, parentDisposable);
repository.myUntrackedFilesHolder.setupVfsListener(project);
repository.setupUpdater();
@@ -23,6 +23,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.changes.ui.SelectFilesDialog;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitVcs;
import git4idea.NotificationManager;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -48,7 +49,7 @@ public class UntrackedFilesNotifier {
final String notificationDesc = createUntrackedFilesOverwrittenDescription(operation, false);
final String dialogDesc = createUntrackedFilesOverwrittenDescription(operation, true);
GitVcs.IMPORTANT_ERROR_NOTIFICATION.createNotification(notificationTitle, notificationDesc, NotificationType.ERROR, new NotificationListener() {
NotificationManager.getInstance(project).notify(GitVcs.IMPORTANT_ERROR_NOTIFICATION, notificationTitle, notificationDesc, NotificationType.ERROR, new NotificationListener() {
@Override public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) {
SelectFilesDialog dlg = new SelectFilesDialog(project, new ArrayList<VirtualFile>(untrackedFiles), dialogDesc, null, false, false) {
@Override protected Action[] createActions() {
@@ -58,8 +59,7 @@ public class UntrackedFilesNotifier {
dlg.setTitle("Untracked Files Preventing " + StringUtil.capitalize(operation));
dlg.show();
}
}).notify(project);
});
}
public static String createUntrackedFilesOverwrittenDescription(@NotNull final String operation, boolean filesAreShown) {
@@ -0,0 +1,549 @@
package git4idea.branch;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsConfiguration;
import com.intellij.openapi.vcs.VcsShowConfirmationOption;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.AbstractVcsTestCase;
import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory;
import com.intellij.testFramework.fixtures.TempDirTestFixture;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcsUtil.VcsUtil;
import git4idea.GitBranch;
import git4idea.GitVcs;
import git4idea.repo.GitRepository;
import git4idea.test.GitTestScenarioGenerator;
import git4idea.test.GitTestUtil;
import git4idea.test.TestMessageManager;
import git4idea.test.TestNotificationManager;
import git4idea.tests.TestDialogHandler;
import git4idea.tests.TestDialogManager;
import git4idea.util.UntrackedFilesNotifier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import static git4idea.test.GitExec.*;
import static org.testng.Assert.*;
/**
* @author Kirill Likhodedov
*/
public class GitBranchOperationsTest extends AbstractVcsTestCase {
private static final String NEW_BRANCH = "new_branch";
private static final String MASTER = "master";
private Collection<GitRepository> myRepositories;
private GitRepository myUltimate;
private GitRepository myCommunity;
private GitRepository myContrib;
private TestMessageManager myMessageManager;
private TestNotificationManager myNotificationManager;
private TestDialogManager myDialogManager;
private TempDirTestFixture myTempDirFixture;
@BeforeMethod
public void setup() throws Exception {
final IdeaTestFixtureFactory fixtureFactory = IdeaTestFixtureFactory.getFixtureFactory();
myTempDirFixture = fixtureFactory.createTempDirTestFixture();
myTempDirFixture.setUp();
final File projectDir = new File(myTempDirFixture.getTempDirPath(), "ultimate");
assertTrue(projectDir.mkdir());
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
try {
initProject(projectDir);
initRepositories(VcsUtil.getVirtualFile(projectDir));
}
catch (Exception e) {
throw new RuntimeException("Exception initializing the test", e);
}
}
});
GitVcs vcs = GitVcs.getInstance(myProject);
assertNotNull(vcs);
myTraceClient = true;
doActionSilently(VcsConfiguration.StandardConfirmation.ADD);
doActionSilently(VcsConfiguration.StandardConfirmation.REMOVE);
myDialogManager = GitTestUtil.registerDialogManager(myProject);
myNotificationManager = GitTestUtil.registerNotificationManager(myProject);
myMessageManager = GitTestUtil.registerMessageManager(myProject);
createAddCommit(myUltimate, "a");
createAddCommit(myCommunity, "a");
createAddCommit(myContrib, "a");
myUltimate.getRoot().refresh(false, true);
}
protected void doActionSilently(final VcsConfiguration.StandardConfirmation op) {
setStandardConfirmation(GitVcs.NAME, op, VcsShowConfirmationOption.Value.DO_ACTION_SILENTLY);
}
@AfterMethod
public void tearDown() throws Exception {
if (myTempDirFixture != null) {
myTempDirFixture.tearDown();
myTempDirFixture = null;
}
}
private void initRepositories(VirtualFile projectDir) throws IOException {
myUltimate = init(myProject, projectDir);
VirtualFile communityDir = createDirInCommand(projectDir, "community");
VirtualFile contribDir = createDirInCommand(projectDir, "contrib");
myCommunity = init(myProject, communityDir);
myContrib = init(myProject, contribDir);
addProjectRoots();
myRepositories = Arrays.asList(myUltimate, myCommunity, myContrib);
}
private void addProjectRoots() {
ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject);
vcsManager.setDirectoryMapping(myUltimate.getRoot().getPath(), GitVcs.NAME);
vcsManager.setDirectoryMapping(myCommunity.getRoot().getPath(), GitVcs.NAME);
vcsManager.setDirectoryMapping(myContrib.getRoot().getPath(), GitVcs.NAME);
}
@Test
public void create_new_branch_without_problems() throws Exception {
doCheckoutNewBranch();
assertNotify(NotificationType.INFORMATION, "Branch new_branch was created");
}
@Test
public void create_new_branch_with_unmerged_files_in_first_repo_should_show_notification() throws Exception {
GitTestScenarioGenerator.prepareUnmergedFiles(myUltimate);
doCheckoutNewBranch();
assertNotify(NotificationType.ERROR, GitBranchOperation.UNMERGED_FILES_ERROR_NOTIFICATION_DESCRIPTION);
}
@Test
public void create_new_branch_with_unmerged_files_in_second_repo_should_propose_to_rollback() throws Exception {
GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity, myContrib);
doCheckoutNewBranch();
assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
}
@Test
public void rollback_create_new_branch_should_delete_branch() throws Exception {
GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity, myContrib);
myMessageManager.nextAnswer(Messages.OK);
doCheckoutNewBranch();
assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
assertBranch("master");
assertTrue(!branch(myUltimate).contains(NEW_BRANCH));
}
@Test
public void deny_rollback_create_new_branch() throws Exception {
GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity, myContrib);
myMessageManager.nextAnswer(Messages.CANCEL);
doCheckoutNewBranch();
assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
assertBranch(myUltimate, NEW_BRANCH);
assertBranch(myCommunity, MASTER);
assertBranch(myContrib, MASTER);
}
@Test
public void checkout_without_problems() throws Exception {
prepareBranchForSimpleCheckout();
doCheckout("feature", null);
assertNotify(NotificationType.INFORMATION, "Checked out feature");
}
@Test
public void checkout_with_unmerged_files_in_first_repo_should_show_notification() throws Exception {
prepareBranchForSimpleCheckout();
GitTestScenarioGenerator.prepareUnmergedFiles(myUltimate);
doCheckout("feature", null);
assertNotify(NotificationType.ERROR, GitBranchOperation.UNMERGED_FILES_ERROR_NOTIFICATION_DESCRIPTION);
}
@Test
public void checkout_with_unmerged_file_in_second_repo_should_propose_to_rollback() throws Exception {
prepareBranchForSimpleCheckout();
GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity);
doCheckout("feature", null);
assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
}
@Test
public void rollback_checkout_should_return_to_previous_branch() throws Exception {
prepareBranchForSimpleCheckout();
GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity);
myMessageManager.nextAnswer(Messages.OK);
doCheckout("feature", null);
assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
assertBranch("master");
}
@Test
public void deny_rollback_checkout_should_do_nothing() throws Exception {
prepareBranchForSimpleCheckout();
GitTestScenarioGenerator.prepareUnmergedFiles(myCommunity);
myMessageManager.nextAnswer(Messages.CANCEL);
doCheckout("feature", null);
assertMessage(GitBranchOperation.UNMERGED_FILES_ERROR_TITLE);
assertBranch(myUltimate, "feature");
assertBranch(myCommunity, "master");
assertBranch(myContrib, "master");
}
@Test
public void checkout_with_untracked_files_overwritten_by_checkout_in_first_repo_should_show_notification() throws Exception {
prepareUntrackedFilesAndBranchWithSameTrackedFiles(myUltimate);
branch(myCommunity, "feature");
branch(myContrib, "feature");
doCheckout("feature", null);
assertNotify(NotificationType.ERROR, UntrackedFilesNotifier.createUntrackedFilesOverwrittenDescription("checkout", false));
}
@Test
public void checkout_with_untracked_files_overwritten_by_checkout_in_second_repo_should_show_rollback_proposal_with_file_list() throws Exception {
prepareUntrackedFilesAndBranchWithSameTrackedFiles(myCommunity);
branch(myUltimate, "feature");
branch(myContrib, "feature");
Class gitCheckoutOperationClass = Class.forName("git4idea.branch.GitCheckoutOperation");
Class[] classes = gitCheckoutOperationClass.getDeclaredClasses();
Class untrackedFilesDialogClass = null;
for (Class aClass : classes) {
if (aClass.getName().endsWith("UntrackedFilesDialog")) {
untrackedFilesDialogClass = aClass;
}
}
assertNotNull(untrackedFilesDialogClass);
final AtomicBoolean dialogShown = new AtomicBoolean();
final Class finalUntrackedFilesDialogClass = untrackedFilesDialogClass;
myDialogManager.registerDialogHandler(untrackedFilesDialogClass, new TestDialogHandler() {
@Override
public int handleDialog(Object dialog) {
if (dialog.getClass().equals(finalUntrackedFilesDialogClass)) {
dialogShown.set(true);
}
return DialogWrapper.CANCEL_EXIT_CODE;
}
});
doCheckout("feature", null);
assertTrue(dialogShown.get());
}
@Test
public void checkout_with_local_changes_overwritten_by_checkout_should_show_smart_checkout_dialog() throws Exception {
prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myUltimate);
branch(myCommunity, "feature");
branch(myContrib, "feature");
final AtomicBoolean dialogShown = new AtomicBoolean();
myDialogManager.registerDialogHandler(GitWouldBeOverwrittenByCheckoutDialog.class, new TestDialogHandler<GitWouldBeOverwrittenByCheckoutDialog>() {
@Override
public int handleDialog(GitWouldBeOverwrittenByCheckoutDialog dialog) {
dialogShown.set(true);
return DialogWrapper.CANCEL_EXIT_CODE;
}
});
doCheckout("feature", null);
assertTrue(dialogShown.get());
}
@Test
public void agree_to_smart_checkout_should_smart_checkout() throws Exception {
prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myUltimate);
prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myCommunity);
prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myContrib);
myDialogManager.registerDialogHandler(GitWouldBeOverwrittenByCheckoutDialog.class,
new TestDialogHandler<GitWouldBeOverwrittenByCheckoutDialog>() {
@Override
public int handleDialog(GitWouldBeOverwrittenByCheckoutDialog dialog) {
return DialogWrapper.OK_EXIT_CODE;
}
});
doCheckout("feature", null);
assertBranch("feature");
for (GitRepository repository : myRepositories) {
refresh(repository);
assertBranch(repository, "feature");
assertEquals(read(repository, "local.txt"), "master\ninitial content\nfeature content\n");
}
}
@Test
public void deny_to_smart_checkout_in_first_repo_should_show_notification() throws Exception {
prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myUltimate);
branch(myCommunity, "feature");
branch(myContrib, "feature");
myDialogManager.registerDialogHandler(GitWouldBeOverwrittenByCheckoutDialog.class, new TestDialogHandler<GitWouldBeOverwrittenByCheckoutDialog>() {
@Override
public int handleDialog(GitWouldBeOverwrittenByCheckoutDialog dialog) {
return DialogWrapper.CANCEL_EXIT_CODE;
}
});
doCheckout("feature", null);
assertNotify(NotificationType.ERROR, "Couldn't checkout feature", stripHtmlAndBreaks("Local changes would be overwritten by checkout." +
"Stash or commit them before checking out a branch."));
assertBranch("master");
}
@Test
public void deny_to_smart_checkout_in_second_repo_should_show_rollback_proposal() throws Exception {
prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(myCommunity);
branch(myUltimate, "feature");
branch(myContrib, "feature");
myDialogManager.registerDialogHandler(GitWouldBeOverwrittenByCheckoutDialog.class, new TestDialogHandler<GitWouldBeOverwrittenByCheckoutDialog>() {
@Override
public int handleDialog(GitWouldBeOverwrittenByCheckoutDialog dialog) {
return DialogWrapper.CANCEL_EXIT_CODE;
}
});
doCheckout("feature", null);
assertMessage("Couldn't checkout feature",
"Local changes would be overwritten by checkout.<br/>Stash or commit them before checking out a branch.<br/>" +
"However checkout has succeeded for the following repositories:<br/>" +
myUltimate.getPresentableUrl() +
"<br/>You may rollback (checkout back to master) not to let branches diverge.",
"Rollback", "Don't rollback");
}
private static void prepareLocalChangesAndBranchWithSameModifiedFilesWithoutConflicts(GitRepository repository) throws IOException {
create(repository, "local.txt", "initial content\n");
addCommit(repository);
checkout(repository, "-b", "feature");
edit(repository, "local.txt", "initial content\nfeature content\n");
addCommit(repository);
checkout(repository, "master");
edit(repository, "local.txt", "master\ninitial content\n");
}
private static void prepareUntrackedFilesAndBranchWithSameTrackedFiles(GitRepository repository) throws IOException {
checkout(repository, "-b", "feature");
createAddCommit(repository, "untracked.txt");
checkout(repository, "master");
create(repository, "untracked.txt");
}
private void prepareBranchForSimpleCheckout() throws IOException {
for (GitRepository repository : myRepositories) {
checkout(repository, "-b", "feature");
createAddCommit(repository, "feature_file.txt");
checkout(repository, "master");
}
}
@Test
public void delete_branch_without_problems() throws Exception {
for (GitRepository repository : myRepositories) {
branch(repository, "master1");
refresh(repository);
}
doDeleteBranch("master1");
assertNotify(NotificationType.INFORMATION, "Deleted branch master1");
}
@Test
public void delete_unmerged_branch_should_show_dialog() throws Exception {
prepareUnmergedBranch(myUltimate, myCommunity, myContrib);
final AtomicBoolean dialogShown = new AtomicBoolean();
myDialogManager.registerDialogHandler(GitBranchIsNotFullyMergedDialog.class, new TestDialogHandler<GitBranchIsNotFullyMergedDialog>() {
@Override public int handleDialog(GitBranchIsNotFullyMergedDialog dialog) {
dialogShown.set(true);
return DialogWrapper.CANCEL_EXIT_CODE;
}
});
doDeleteBranch("unmerged_branch");
assertTrue(dialogShown.get());
}
@Test
public void ok_in_unmerged_branch_dialog_should_force_delete_branch() throws Exception {
prepareUnmergedBranch(myUltimate, myCommunity, myContrib);
registerNotFullyMergedDialog(DialogWrapper.OK_EXIT_CODE);
doDeleteBranch("unmerged_branch");
for (GitRepository repository : myRepositories) {
assertTrue(!branch(repository).contains("unmerged_branch"));
}
}
@Test
public void cancel_in_unmerged_branch_dialog_in_first_repository_should_show_notification() throws Exception {
prepareUnmergedBranch(myUltimate, myContrib);
branch(myCommunity, "unmerged_branch");
registerNotFullyMergedDialog(DialogWrapper.CANCEL_EXIT_CODE);
doDeleteBranch("unmerged_branch");
assertNotify(NotificationType.ERROR, "Branch unmerged_branch wasn't deleted", "This branch is not fully merged to master");
}
@Test
public void cancel_in_unmerged_branch_dialog_in_not_first_repository_should_show_rollback_proposal() throws Exception {
branch(myUltimate, "unmerged_branch");
prepareUnmergedBranch(myCommunity, myContrib);
registerNotFullyMergedDialog(DialogWrapper.CANCEL_EXIT_CODE);
doDeleteBranch("unmerged_branch");
assertMessage(String.format("Branch %s wasn't deleted", "unmerged_branch"));
}
@Test
public void rollback_delete_branch_should_recreate_branches() throws Exception {
branch(myUltimate, "unmerged_branch");
prepareUnmergedBranch(myCommunity);
branch(myContrib, "unmerged_branch");
registerNotFullyMergedDialog(DialogWrapper.CANCEL_EXIT_CODE);
myMessageManager.nextAnswer(Messages.OK);
doDeleteBranch("unmerged_branch");
for (GitRepository repository : myRepositories) {
assertTrue(branch(repository).contains("unmerged_branch"));
}
}
@Test
public void deny_rollback_delete_branch_should_do_nothing() throws Exception {
branch(myUltimate, "unmerged_branch");
prepareUnmergedBranch(myCommunity);
branch(myContrib, "unmerged_branch");
registerNotFullyMergedDialog(DialogWrapper.CANCEL_EXIT_CODE);
myMessageManager.nextAnswer(Messages.CANCEL);
doDeleteBranch("unmerged_branch");
assertTrue(branch(myCommunity).contains("unmerged_branch"));
assertTrue(branch(myContrib).contains("unmerged_branch"));
assertTrue(!branch(myUltimate).contains("unmerged_branch"));
}
private void registerNotFullyMergedDialog(final int answer) {
myDialogManager.registerDialogHandler(GitBranchIsNotFullyMergedDialog.class, new TestDialogHandler<GitBranchIsNotFullyMergedDialog>() {
@Override
public int handleDialog(GitBranchIsNotFullyMergedDialog dialog) {
return answer;
}
});
}
private static void prepareUnmergedBranch(GitRepository... repositories) throws IOException {
for (GitRepository repository : repositories) {
checkout(repository, "-b", "unmerged_branch");
createAddCommit(repository, "unmerged_branch_file");
checkout(repository, "master");
refresh(repository);
}
}
private void doCheckoutNewBranch() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
callPrivateBranchOperationsProcessorMethod("doCheckoutNewBranch", NEW_BRANCH);
}
private void callPrivateBranchOperationsProcessorMethod(String methodName, String branchName) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
// call private doCheckoutNewBranch instead of public checkoutNewBranch to avoid dealing with background process creation
// same for other branch operations
GitBranchOperationsProcessor processor = new GitBranchOperationsProcessor(myProject, myRepositories, myCommunity);
Method method = GitBranchOperationsProcessor.class.getDeclaredMethod(methodName, String.class, ProgressIndicator.class);
method.setAccessible(true);
method.invoke(processor, branchName, new EmptyProgressIndicator());
}
private void doDeleteBranch(@NotNull String branchName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
callPrivateBranchOperationsProcessorMethod("doDelete", branchName);
}
private void doCheckout(@NotNull String branchName, @Nullable String newBranch) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
GitBranchOperationsProcessor processor = new GitBranchOperationsProcessor(myProject, myRepositories, myCommunity);
Method doCheckout = GitBranchOperationsProcessor.class.getDeclaredMethod("doCheckout", ProgressIndicator.class, String.class, String.class);
doCheckout.setAccessible(true);
doCheckout.invoke(processor, new EmptyProgressIndicator(), branchName, newBranch);
}
private void assertBranch(String branch) {
for (GitRepository repository : myRepositories) {
assertBranch(repository, branch);
}
}
private static void assertBranch(GitRepository repository, String branchName) {
GitBranch currentBranch = repository.getCurrentBranch();
assertNotNull(currentBranch);
assertEquals(currentBranch.getName(), branchName);
}
private void assertNotify(NotificationType type, String content) {
assertNotify(type, null, content);
}
private void assertNotify(NotificationType type, @Nullable String title, String content) {
Notification notification = myNotificationManager.getLastNotification();
assertNotNull(notification);
assertEquals(stripHtmlAndBreaks(notification.getContent()), stripHtmlAndBreaks(content));
assertEquals(notification.getType(), type);
if (title != null) {
assertEquals(stripHtmlAndBreaks(notification.getTitle()), stripHtmlAndBreaks(title));
}
}
@NotNull
private static String stripHtmlAndBreaks(@NotNull String text) {
return StringUtil.stripHtml(text, true).replace("\n", "");
}
private void assertMessage(String title) {
assertMessage(title, null, null, null);
}
private void assertMessage(@Nullable String title, @Nullable String description, @Nullable String yesButton, @Nullable String noButton) {
TestMessageManager.Message message = myMessageManager.getLastMessage();
assertNotNull(message);
if (title != null) {
assertEquals(message.getTitle(), title);
}
if (description != null) {
assertEquals(message.getDescription(), description);
}
if (yesButton != null) {
assertEquals(message.getYesText(), yesButton);
}
if (noButton != null) {
assertEquals(message.getNoText(), noButton);
}
}
}
@@ -0,0 +1,117 @@
/*
* 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.test;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.AbstractVcsTestCase;
import com.intellij.testFramework.VfsTestUtil;
import com.intellij.util.ui.UIUtil;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
/**
* @author Kirill Likhodedov
*/
public class GitExec {
public static void refresh(@NotNull GitRepository repository) {
repository.getRoot().refresh(false, true);
}
public static GitRepository init(@NotNull Project project, @NotNull VirtualFile root) throws IOException {
new GitTestRunEnv(new File(root.getPath())).run("init");
root.refresh(false, true);
return GitRepository.getFullInstance(root, project, project);
}
public static void create(@NotNull GitRepository repository, @NotNull String filePath) {
create(repository, filePath, "content");
}
public static void create(final GitRepository repository, final String filePath, @NotNull final String content) {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override public void run() {
VfsTestUtil.createFile(repository.getRoot(), filePath, content);
}
});
}
public static void edit(final GitRepository repository, String filePath, final String newContent) {
final VirtualFile file = repository.getRoot().findFileByRelativePath(filePath);
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override public void run() {
AbstractVcsTestCase.editFileInCommand(repository.getProject(), file, newContent);
}
});
}
@NotNull
public static String read(final @NotNull GitRepository repository, @NotNull String filePath) throws IOException {
final VirtualFile file = repository.getRoot().findFileByRelativePath(filePath);
assert file != null;
return new String(file.contentsToByteArray());
}
public static void add(@NotNull GitRepository repository, @NotNull String filePath) throws IOException {
run(repository, "add", filePath);
}
public static void add(@NotNull GitRepository repository) throws IOException {
run(repository, "add", ".");
}
public static void commit(@NotNull GitRepository repository) throws IOException {
run(repository, "commit", "-m", "message");
}
public static void addCommit(@NotNull GitRepository repository, @NotNull String filePath) throws IOException {
add(repository, filePath);
commit(repository);
}
public static void addCommit(@NotNull GitRepository repository) throws IOException {
add(repository);
commit(repository);
}
public static void createAddCommit(@NotNull GitRepository repository, @NotNull String filePath) throws IOException {
create(repository, filePath);
addCommit(repository, filePath);
}
@NotNull
public static String branch(@NotNull GitRepository repository, String... params) throws IOException {
return run(repository, "branch", params);
}
public static void checkout(@NotNull GitRepository repository, String... params) throws IOException {
run(repository, "checkout", params);
}
public static void merge(@NotNull GitRepository repository, @NotNull String branch) throws IOException {
run(repository, "merge", branch);
}
@NotNull
private static String run(@NotNull GitRepository repository, @NotNull String command, String... params) throws IOException {
return new GitTestRunEnv(new File(repository.getRoot().getPath())).run(command, params);
}
}
@@ -0,0 +1,57 @@
/*
* 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.test;
import git4idea.repo.GitRepository;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import static git4idea.test.GitExec.*;
/**
* @author Kirill Likhodedov
*/
public class GitTestScenarioGenerator {
public static void prepareUnmergedFiles(@NotNull GitRepository... repositories) throws IOException {
for (GitRepository repository : repositories) {
String unmergedFile = "unmerged";
create(repository, unmergedFile, "master content");
addCommit(repository, unmergedFile);
checkoutFeatureBranch(repository);
edit(repository, unmergedFile, "feature content");
addCommit(repository, unmergedFile);
checkout(repository, "master");
edit(repository, unmergedFile, "master feature");
addCommit(repository, unmergedFile);
merge(repository, "feature");
refresh(repository);
}
}
private static void checkoutFeatureBranch(GitRepository repository) throws IOException {
String branches = branch(repository);
if (!branches.contains("feature")) {
checkout(repository, "-b", "feature");
} else {
checkout(repository, "feature");
}
}
}
@@ -22,9 +22,14 @@ 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 git4idea.DialogManager;
import git4idea.MessageManager;
import git4idea.NotificationManager;
import git4idea.tests.GitTestRepository;
import git4idea.tests.TestDialogManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.picocontainer.MutablePicoContainer;
import java.io.File;
import java.io.FilenameFilter;
@@ -227,4 +232,33 @@ public class GitTestUtil {
public static String stringifyActualExpected(@NotNull Object actual, @NotNull Object expected) {
return "\nExpected:\n" + expected + "\nActual:\n" + actual;
}
/**
* Registers {@link git4idea.tests.TestDialogManager} as the {@link git4idea.DialogManager} implementation.
* @param project
*/
public static TestDialogManager registerDialogManager(@NotNull Project project) {
final String key = "git4idea.DialogManager";
final MutablePicoContainer picoContainer = (MutablePicoContainer) project.getPicoContainer();
picoContainer.unregisterComponent(key);
picoContainer.registerComponentImplementation(key, TestDialogManager.class);
return (TestDialogManager)DialogManager.getInstance(project);
}
public static TestMessageManager registerMessageManager(@NotNull Project project) {
final String key = "git4idea.MessageManager";
final MutablePicoContainer picoContainer = (MutablePicoContainer) project.getPicoContainer();
picoContainer.unregisterComponent(key);
picoContainer.registerComponentImplementation(key, TestMessageManager.class);
return (TestMessageManager)MessageManager.getInstance(project);
}
public static TestNotificationManager registerNotificationManager(Project project) {
final String key = "git4idea.NotificationManager";
final MutablePicoContainer picoContainer = (MutablePicoContainer) project.getPicoContainer();
picoContainer.unregisterComponent(key);
picoContainer.registerComponentImplementation(key, TestNotificationManager.class);
return (TestNotificationManager)NotificationManager.getInstance(project);
}
}
@@ -0,0 +1,85 @@
/*
* 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.test;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import git4idea.MessageManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author Kirill Likhodedov
*/
public class TestMessageManager extends MessageManager {
private Message myLastMessage;
private int myNextAnswer = DEFAULT_ANSWER;
private static final int DEFAULT_ANSWER = Messages.CANCEL;
public static class Message {
@NotNull private final String myTitle;
@NotNull private final String myDescription;
@NotNull private final String myYesText;
@NotNull private final String myNoText;
public Message(@NotNull String title, @NotNull String description, @NotNull String yesText, @NotNull String noText) {
myTitle = title;
myDescription = description;
myYesText = yesText;
myNoText = noText;
}
@NotNull
public String getTitle() {
return myTitle;
}
@NotNull
public String getDescription() {
return myDescription;
}
@NotNull
public String getYesText() {
return myYesText;
}
@NotNull
public String getNoText() {
return myNoText;
}
}
@Nullable
public Message getLastMessage() {
return myLastMessage;
}
public void nextAnswer(int answer) {
myNextAnswer = answer;
}
@Override
protected int doShowYesNoDialog(Project project, String description, String title, String yesText, String noText, @Nullable Icon icon) {
myLastMessage = new Message(title, description, yesText, noText);
return myNextAnswer;
}
}
@@ -0,0 +1,51 @@
/*
* 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.test;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationGroup;
import com.intellij.notification.NotificationListener;
import com.intellij.notification.NotificationType;
import com.intellij.openapi.project.Project;
import git4idea.NotificationManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* @author Kirill Likhodedov
*/
public class TestNotificationManager extends NotificationManager {
private Notification myLastNotification;
public TestNotificationManager(@NotNull Project project) {
super(project);
}
public Notification getLastNotification() {
return myLastNotification;
}
public void notify(@NotNull NotificationGroup notificationGroup, @NotNull String title, @NotNull String message, @NotNull NotificationType type) {
notify(notificationGroup, title, message, type, null);
}
public void notify(@NotNull NotificationGroup notificationGroup, @NotNull String title, @NotNull String message,
@NotNull NotificationType type, @Nullable NotificationListener listener) {
myLastNotification = createNotification(notificationGroup, title, message, type, listener);
}
}
@@ -23,11 +23,10 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.CheckboxTreeBase;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.ui.GuiUtils;
import git4idea.DialogManager;
import git4idea.config.GitVcsSettings;
import git4idea.test.GitTestUtil;
import git4idea.ui.GitConvertFilesDialog;
import org.jetbrains.annotations.Nullable;
import org.picocontainer.MutablePicoContainer;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@@ -63,7 +62,7 @@ public class GitLineSeparatorsConverterTest extends GitTest {
myChangeListManager = ChangeListManagerImpl.getInstanceImpl(myProject);
mySettings = GitVcsSettings.getInstance(myProject);
myCodeStyleSeparator = CodeStyleFacade.getInstance(myProject).getLineSeparator();
myDialogManager = registerDialogManager();
myDialogManager = GitTestUtil.registerDialogManager(myProject);
unixFile = createFileInCommand("unix_file.txt", "Unix File\n");
winFile = createFileInCommand("win_file.txt", "Windows File\r\n");
@@ -193,17 +192,6 @@ public class GitLineSeparatorsConverterTest extends GitTest {
return null;
}
/**
* Registers {@link TestDialogManager} as the {@link DialogManager} implementation.
*/
private TestDialogManager registerDialogManager() {
final String key = "git4idea.DialogManager";
final MutablePicoContainer picoContainer = (MutablePicoContainer) myProject.getPicoContainer();
picoContainer.unregisterComponent(key);
picoContainer.registerComponentImplementation(key, TestDialogManager.class);
return (TestDialogManager)DialogManager.getInstance(myProject);
}
/**
* Commits via the ChangeListManager.
*/
+1
View File
@@ -2,6 +2,7 @@
<suite name="Git Suite">
<test name="Git Test">
<packages>
<package name="git4idea.branch"/>
<package name="git4idea.history"/>
<package name="git4idea.repo"/>
<package name="git4idea.validators"/>
@@ -210,6 +210,7 @@ public class TestNGConfigurationEditor extends SettingsEditor<TestNGConfiguratio
}
model.setType(TestType.CLASS);
addListener.addActionListener(new AddTestListenerListener());
addListener.setEnabled(!project.isDefault());
removeListener.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event) {
@@ -101,11 +101,11 @@
</component>
<component id="b9ad7" class="javax.swing.JCheckBox" binding="myResizeHeaders">
<constraints>
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
<grid row="5" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<margin top="10" left="0" bottom="2" right="2"/>
<text value="Resize column and row headers over mouse"/>
<text value="&amp;Resize column and row headers with mouse"/>
</properties>
</component>
</children>
@@ -546,7 +546,7 @@ action.GuiDesigner.ReloadCustomComponents.text=Reload Custom Components
action.GuiDesigner.ReloadCustomComponents.description=Reload modified custom component classes and recreate forms
action.NewDialog.text=Dialog
action.NewForm.text=GUI Form
default.field.accessibility=Default accessibility for UI-bound fields:
default.field.accessibility=Default accessibility &for UI-bound fields\:
plugin.com.intellij.uiDesigner.description=Provides visual editing support for Swing UI forms
group.GuiDesigner.MainToolbarActions.text=UI Designer Toolbar Actions
group.GuiDesigner.NewActions.text=New GUI Designer Actions