diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java
index 3e398cc99a8b..e35aa3aa80a5 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefJavaUtilImpl.java
@@ -211,6 +211,14 @@ public class RefJavaUtilImpl extends RefJavaUtil{
PsiMethod psiMethod = (PsiMethod)psiResolved;
RefMethodImpl refMethod = (RefMethodImpl)refResolved;
+ if (refExpression instanceof PsiMethodReferenceExpression) {
+ PsiType returnType = psiMethod.getReturnType();
+ if (!psiMethod.isConstructor() && returnType != PsiType.VOID) {
+ refMethod.setReturnValueUsed(true);
+ addTypeReference(psiFrom, returnType, refFrom.getRefManager());
+ }
+ return;
+ }
PsiMethodCallExpression call = PsiTreeUtil.getParentOfType(
refExpression,
PsiMethodCallExpression.class
diff --git a/java/java-tests/testData/inspection/unusedReturnValue/methodReference/expected.xml b/java/java-tests/testData/inspection/unusedReturnValue/methodReference/expected.xml
new file mode 100644
index 000000000000..4704d91e891d
--- /dev/null
+++ b/java/java-tests/testData/inspection/unusedReturnValue/methodReference/expected.xml
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/java/java-tests/testData/inspection/unusedReturnValue/methodReference/src/B.java b/java/java-tests/testData/inspection/unusedReturnValue/methodReference/src/B.java
new file mode 100644
index 000000000000..769f894607d3
--- /dev/null
+++ b/java/java-tests/testData/inspection/unusedReturnValue/methodReference/src/B.java
@@ -0,0 +1,13 @@
+import java.util.function.Consumer;
+import java.util.function.DoubleSupplier;
+
+class B {
+
+ public void test(Consumer consumer) {
+ consumer.accept(this::method);
+ }
+
+ private double method() {
+ return 1;
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/UnusedReturnValueTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/UnusedReturnValueTest.java
index 6753f529f2d2..74f27e0b0c11 100644
--- a/java/java-tests/testSrc/com/intellij/codeInspection/UnusedReturnValueTest.java
+++ b/java/java-tests/testSrc/com/intellij/codeInspection/UnusedReturnValueTest.java
@@ -12,6 +12,10 @@ package com.intellij.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.unusedReturnValue.UnusedReturnValue;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.roots.LanguageLevelProjectExtension;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.InspectionTestCase;
public class UnusedReturnValueTest extends InspectionTestCase {
@@ -39,7 +43,17 @@ public class UnusedReturnValueTest extends InspectionTestCase {
doTest();
}
-
+ public void testMethodReference() throws Exception {
+ doTest();
+ }
+
+ @Override
+ protected Sdk getTestProjectSdk() {
+ Sdk sdk = IdeaTestUtil.getMockJdk18();
+ LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_8);
+ return sdk;
+ }
+
public void testSimpleSetter() throws Exception {
try {
myTool.IGNORE_BUILDER_PATTERN = true;
diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java
index 1733ead28937..26b11476cf39 100644
--- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java
+++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/compiler/JavaCompilingVisitor.java
@@ -589,18 +589,6 @@ public class JavaCompilingVisitor extends JavaRecursiveElementWalkingVisitor {
}
private MatchingStrategy findStrategy(PsiElement el) {
- // identify matching strategy
- final MatchingHandler handler = myCompilingVisitor.getContext().getPattern().getHandler(el);
-
- //if (handler instanceof SubstitutionHandler) {
- // final SubstitutionHandler shandler = (SubstitutionHandler) handler;
- if (handler.getFilter() instanceof SymbolNodeFilter ||
- handler.getFilter() instanceof TypedSymbolNodeFilter
- ) {
- return SymbolMatchingStrategy.getInstance();
- }
- //}
-
if (el instanceof PsiDocComment) {
return JavaDocMatchingStrategy.getInstance();
}
diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/ExprMatchingStrategy.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/ExprMatchingStrategy.java
index a1bebac82a42..4a38b7f40193 100644
--- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/ExprMatchingStrategy.java
+++ b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/ExprMatchingStrategy.java
@@ -7,6 +7,27 @@ import com.intellij.psi.*;
*/
public class ExprMatchingStrategy extends MatchingStrategyBase {
+ @Override
+ public void visitTypeElement(PsiTypeElement type) {
+ result = true;
+ }
+
+ @Override public void visitReferenceParameterList(PsiReferenceParameterList psiReferenceParameterList) {
+ result = true;
+ }
+
+ @Override public void visitReferenceElement(PsiJavaCodeReferenceElement psiJavaCodeReferenceElement) {
+ result = true;
+ }
+
+ @Override public void visitTypeParameterList(PsiTypeParameterList psiTypeParameterList) {
+ result = true;
+ }
+
+ @Override public void visitReferenceList(final PsiReferenceList list) {
+ result = true;
+ }
+
@Override public void visitAnnotation(final PsiAnnotation annotation) {
result = true;
}
@@ -55,6 +76,11 @@ public class ExprMatchingStrategy extends MatchingStrategyBase {
result = true;
}
+ @Override
+ public void visitPackageStatement(PsiPackageStatement statement) {
+ result = true;
+ }
+
// finding parameters
@Override public void visitParameterList(final PsiParameterList list) {
result = true;
diff --git a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/SymbolMatchingStrategy.java b/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/SymbolMatchingStrategy.java
deleted file mode 100644
index f31ee37e8aed..000000000000
--- a/java/structuralsearch-java/src/com/intellij/structuralsearch/impl/matcher/strategies/SymbolMatchingStrategy.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.intellij.structuralsearch.impl.matcher.strategies;
-
-import com.intellij.psi.*;
-
-/**
- * CommonStrategy to match symbols
- */
-public class SymbolMatchingStrategy extends ExprMatchingStrategy {
- @Override public void visitReferenceList(final PsiReferenceList list) {
- result = true;
- }
-
- @Override public void visitAnnotation(final PsiAnnotation annotation) {
- result = true;
- }
-
- @Override public void visitAnnotationParameterList(final PsiAnnotationParameterList list) {
- result = true;
- }
-
- @Override public void visitModifierList(final PsiModifierList list) {
- result = true;
- }
-
- @Override public void visitNameValuePair(final PsiNameValuePair pair) {
- result = true;
- }
-
- @Override public void visitTypeParameterList(PsiTypeParameterList psiTypeParameterList) {
- result = true;
- }
-
- @Override public void visitTypeElement(PsiTypeElement psiTypeElement) {
- result = true;
- }
-
- @Override public void visitReferenceElement(PsiJavaCodeReferenceElement psiJavaCodeReferenceElement) {
- result = true;
- }
-
- @Override public void visitReferenceParameterList(PsiReferenceParameterList psiReferenceParameterList) {
- result = true;
- }
-
- private SymbolMatchingStrategy() {}
-
- private static class SymbolMatchingStrategyHolder {
- private static final SymbolMatchingStrategy instance = new SymbolMatchingStrategy();
- }
-
- public static MatchingStrategy getInstance() {
- return SymbolMatchingStrategyHolder.instance;
- }
-}
diff --git a/platform/lang-api/src/com/intellij/refactoring/util/MoveRenameUsageInfo.java b/platform/lang-api/src/com/intellij/refactoring/util/MoveRenameUsageInfo.java
index f8a3f5fc6cc7..618931700adb 100644
--- a/platform/lang-api/src/com/intellij/refactoring/util/MoveRenameUsageInfo.java
+++ b/platform/lang-api/src/com/intellij/refactoring/util/MoveRenameUsageInfo.java
@@ -22,6 +22,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.usageView.UsageInfo;
+import com.intellij.util.Function;
import org.jetbrains.annotations.Nullable;
public class MoveRenameUsageInfo extends UsageInfo{
@@ -90,18 +91,49 @@ public class MoveRenameUsageInfo extends UsageInfo{
public PsiReference getReference() {
if (myReference != null) {
final PsiElement element = myReference.getElement();
- if (element != null && element.isValid()) return myReference;
+ if (element != null && element.isValid()) {
+ if (myReferenceRangeMarker == null) {
+ return myReference;
+ }
+
+ final PsiReference reference = checkReferenceRange(element, new Function() {
+ @Override
+ public PsiReference fun(Integer start) {
+ return myReference;
+ }
+ });
+
+ if (reference != null) {
+ return reference;
+ }
+ }
}
if (myReferenceRangeMarker == null) return null;
final PsiElement element = getElement();
- if (element == null) return null;
+ if (element == null || !element.isValid()) {
+ return null;
+ }
+ return checkReferenceRange(element, new Function() {
+ @Override
+ public PsiReference fun(Integer start) {
+ return element.findReferenceAt(start);
+ }
+ });
+ }
+
+ @Nullable
+ private PsiReference checkReferenceRange(PsiElement element, Function fn) {
final int start = myReferenceRangeMarker.getStartOffset() - element.getTextRange().getStartOffset();
final int end = myReferenceRangeMarker.getEndOffset() - element.getTextRange().getStartOffset();
- final PsiReference reference = element.findReferenceAt(start);
- if (reference == null) return null;
+ final PsiReference reference = fn.fun(start);
+ if (reference == null) {
+ return null;
+ }
final TextRange rangeInElement = reference.getRangeInElement();
- if (rangeInElement.getStartOffset() != start || rangeInElement.getEndOffset() != end) return null;
+ if (rangeInElement.getStartOffset() != start || rangeInElement.getEndOffset() != end) {
+ return null;
+ }
return reference;
}
}
diff --git a/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java b/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java
index 5c9449dd3863..a94f8daa11f6 100644
--- a/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java
+++ b/platform/projectModel-impl/src/com/intellij/core/CoreProjectLoader.java
@@ -34,6 +34,7 @@ import org.jdom.Element;
import org.jdom.JDOMException;
import org.jetbrains.annotations.NotNull;
+import java.io.FileNotFoundException;
import java.io.IOException;
/**
@@ -55,9 +56,13 @@ public class CoreProjectLoader {
private static void loadDirectoryProject(MockProject project, VirtualFile projectDir) throws IOException, JDOMException,
InvalidDataException {
VirtualFile dotIdea = projectDir.findChild(Project.DIRECTORY_STORE_FOLDER);
- assert dotIdea != null;
+ if (dotIdea == null)
+ throw new FileNotFoundException("Missing '" + Project.DIRECTORY_STORE_FOLDER + "' in " + projectDir.getPath());
+
VirtualFile modulesXml = dotIdea.findChild("modules.xml");
- assert modulesXml != null;
+ if (modulesXml == null)
+ throw new FileNotFoundException("Missing 'modules.xml' in " + dotIdea.getPath());
+
StorageData storageData = loadStorageFile(project, modulesXml);
final Element moduleManagerState = storageData.getState("ProjectModuleManager");
if (moduleManagerState == null) {
@@ -67,7 +72,8 @@ public class CoreProjectLoader {
moduleManager.loadState(moduleManagerState);
VirtualFile miscXml = dotIdea.findChild("misc.xml");
- assert miscXml != null;
+ if (miscXml == null)
+ throw new FileNotFoundException("Missing 'misc.xml' in " + dotIdea.getPath());
storageData = loadStorageFile(project, miscXml);
final Element projectRootManagerState = storageData.getState("ProjectRootManager");
if (projectRootManagerState == null) {
diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/BreakpointManager.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/BreakpointManager.java
index be9307493253..24cea6aae19c 100644
--- a/platform/script-debugger/backend/src/org/jetbrains/debugger/BreakpointManager.java
+++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/BreakpointManager.java
@@ -7,8 +7,10 @@ import org.jetbrains.concurrency.Promise;
import java.util.EventListener;
public interface BreakpointManager {
+ @NotNull
Breakpoint setBreakpoint(@NotNull BreakpointTarget target, int line, int column, @Nullable String condition, int ignoreCount, boolean enabled);
+ @NotNull
Promise remove(@NotNull Breakpoint breakpoint);
@Nullable
diff --git a/platform/script-debugger/backend/src/org/jetbrains/debugger/BreakpointManagerBase.java b/platform/script-debugger/backend/src/org/jetbrains/debugger/BreakpointManagerBase.java
index bf5c19e39267..87d374204439 100644
--- a/platform/script-debugger/backend/src/org/jetbrains/debugger/BreakpointManagerBase.java
+++ b/platform/script-debugger/backend/src/org/jetbrains/debugger/BreakpointManagerBase.java
@@ -60,6 +60,7 @@ public abstract class BreakpointManagerBase> impleme
protected abstract Promise doSetBreakpoint(@NotNull BreakpointTarget target, @NotNull T breakpoint);
+ @NotNull
@Override
public Breakpoint setBreakpoint(@NotNull final BreakpointTarget target, int line, int column, @Nullable String condition, int ignoreCount, boolean enabled) {
final T breakpoint = createBreakpoint(target, line, column, condition, ignoreCount, enabled);
@@ -81,6 +82,7 @@ public abstract class BreakpointManagerBase> impleme
return breakpoint;
}
+ @NotNull
@Override
public Promise remove(@NotNull Breakpoint breakpoint) {
@SuppressWarnings("unchecked")
@@ -131,4 +133,10 @@ public abstract class BreakpointManagerBase> impleme
public FunctionSupport getFunctionSupport() {
return null;
}
+
+ @Nullable
+ @Override
+ public ScriptRegExpSupport getScriptRegExpSupport() {
+ return null;
+ }
}
\ No newline at end of file
diff --git a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java
index 002d0cea3aee..6398627faff1 100644
--- a/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java
+++ b/platform/structuralsearch/testSource/com/intellij/structuralsearch/StructuralSearchTest.java
@@ -2445,12 +2445,22 @@ public class StructuralSearchTest extends StructuralSearchTestCase {
"}\n";
assertEquals("Find all annotations", 4, findMatchesCount(source2, "@'_Annotation"));
- String source3 = "class A {\n" +
+ String source3 = "class A<@HH T> extends @HH Object {\n" +
" @HH final String s = (@HH String) new @HH Object();\n" +
" final String t = (String) new Object();\n" +
+ " Map<@HH String, @HH List<@HH String>> map;\n" +
"}\n";
assertEquals("Find annotated casts", 1, findMatchesCount(source3, "(@'_A 'Cast) '_Expression"));
assertEquals("Find annotated new expressions", 1, findMatchesCount(source3, "new @'_A 'Type()"));
+ assertEquals("Find all annotations 2", 8, findMatchesCount(source3, "@'_Annotation"));
+
+ // package-info.java
+ final String source4 = "/**\n" +
+ " * documentation\n" +
+ " */\n" +
+ "@Deprecated\n" +
+ "package one.two;";
+ assertEquals("Find annotation on package statement", 1, findMatchesCount(source4, "@'_Annotation", true));
}
public void testBoxingAndUnboxing() {
diff --git a/platform/testFramework/src/com/intellij/TestCaseLoader.java b/platform/testFramework/src/com/intellij/TestCaseLoader.java
index 0027755981ee..e4c047f3c118 100644
--- a/platform/testFramework/src/com/intellij/TestCaseLoader.java
+++ b/platform/testFramework/src/com/intellij/TestCaseLoader.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2000-2013 JetBrains s.r.o.
+ * Copyright 2000-2015 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.
@@ -171,7 +171,7 @@ public class TestCaseLoader {
public void loadTestCases(final Collection classNamesIterator) {
for (String className : classNamesIterator) {
try {
- Class candidateClass = Class.forName(className);
+ Class candidateClass = Class.forName(className, false, getClass().getClassLoader());
addClassIfTestCase(candidateClass);
}
catch (ClassNotFoundException e) {
diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java
index b4c8fbb9f90e..d5d8b3ea5e00 100644
--- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java
+++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTreeList.java
@@ -26,6 +26,7 @@ import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.EmptyRunnable;
import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
@@ -362,15 +363,15 @@ public abstract class ChangesTreeList extends JPanel implements TypeSafeDataP
if (myProject.isDisposed()) return;
TreeUtil.expandAll(myTree);
- int listSelection = 0;
- int scrollRow = -1;
+ int selectedListRow = 0;
+ int selectedTreeRow = -1;
if (myShowCheckboxes) {
if (myIncludedChanges.size() > 0) {
for (int i = 0; i < sortedChanges.size(); i++) {
T t = sortedChanges.get(i);
if (myIncludedChanges.contains(t)) {
- listSelection = i;
+ selectedListRow = i;
break;
}
}
@@ -393,46 +394,32 @@ public abstract class ChangesTreeList extends JPanel implements TypeSafeDataP
@SuppressWarnings("unchecked")
final CheckboxTree.NodeState state = getNodeStatus(node);
if (state == CheckboxTree.NodeState.FULL && node.isLeaf()) {
- scrollRow = myTree.getRowForPath(new TreePath(node.getPath()));
+ selectedTreeRow = myTree.getRowForPath(new TreePath(node.getPath()));
break;
}
}
}
} else {
if (toSelect != null) {
- ChangesBrowserNode root = (ChangesBrowserNode)model.getRoot();
- final int[] rowToSelect = new int[] {-1};
- TreeUtil.traverse(root, new TreeUtil.Traverse() {
- @Override
- public boolean accept(Object node) {
- if (node instanceof DefaultMutableTreeNode) {
- Object userObject = ((DefaultMutableTreeNode)node).getUserObject();
- if (userObject instanceof Change) {
- Change change = (Change)userObject;
- VirtualFile virtualFile = change.getVirtualFile();
- if ((virtualFile != null && virtualFile.equals(toSelect)) || seemsToBeMoved(change, toSelect)) {
- TreeNode[] path = ((DefaultMutableTreeNode)node).getPath();
- rowToSelect[0] = myTree.getRowForPath(new TreePath(path));
- }
- }
- }
-
- return rowToSelect[0] == -1;
- }
- });
-
- scrollRow = rowToSelect[0] == -1 ? scrollRow : rowToSelect[0];
+ int rowInTree = findRowContainingFile((TreeNode)model.getRoot(), toSelect);
+ if (rowInTree > -1) {
+ selectedTreeRow = rowInTree;
+ }
+ int rowInList = findRowContainingFile(myList.getModel(), toSelect);
+ if (rowInList > -1) {
+ selectedListRow = rowInList;
+ }
}
}
if (changes.size() > 0) {
- myList.setSelectedIndex(listSelection);
- myList.ensureIndexIsVisible(listSelection);
+ myList.setSelectedIndex(selectedListRow);
+ myList.ensureIndexIsVisible(selectedListRow);
- if (scrollRow >= 0) {
- myTree.setSelectionRow(scrollRow);
+ if (selectedTreeRow >= 0) {
+ myTree.setSelectionRow(selectedTreeRow);
}
- TreeUtil.showRowCentered(myTree, scrollRow, false);
+ TreeUtil.showRowCentered(myTree, selectedTreeRow, false);
}
}
};
@@ -443,6 +430,50 @@ public abstract class ChangesTreeList extends JPanel implements TypeSafeDataP
}
}
+ private static int findRowContainingFile(@NotNull ListModel listModel, @Nullable final VirtualFile toSelect) {
+ if (toSelect == null) {
+ return -1;
+ }
+
+ for (int i = 0; i < listModel.getSize(); i++) {
+ Object item = listModel.getElementAt(i);
+ if (item instanceof Change && matches((Change)item, toSelect)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private int findRowContainingFile(@NotNull TreeNode root, @Nullable final VirtualFile toSelect) {
+ if (toSelect == null) {
+ return -1;
+ }
+
+ final Ref row = Ref.create(-1);
+ TreeUtil.traverse(root, new TreeUtil.Traverse() {
+ @Override
+ public boolean accept(Object node) {
+ if (node instanceof DefaultMutableTreeNode) {
+ Object userObject = ((DefaultMutableTreeNode)node).getUserObject();
+ if (userObject instanceof Change) {
+ if (matches((Change)userObject, toSelect)) {
+ TreeNode[] path = ((DefaultMutableTreeNode)node).getPath();
+ row.set(myTree.getRowForPath(new TreePath(path)));
+ }
+ }
+ }
+
+ return row.get() == -1;
+ }
+ });
+ return row.get();
+ }
+
+ private static boolean matches(@NotNull Change change, @NotNull VirtualFile file) {
+ VirtualFile virtualFile = change.getVirtualFile();
+ return virtualFile != null && virtualFile.equals(file) || seemsToBeMoved(change, file);
+ }
+
private static boolean seemsToBeMoved(Change change, VirtualFile toSelect) {
ContentRevision afterRevision = change.getAfterRevision();
if (afterRevision == null) return false;
diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java
index c12356977209..ae795ee22439 100644
--- a/plugins/git4idea/src/git4idea/GitVcs.java
+++ b/plugins/git4idea/src/git4idea/GitVcs.java
@@ -155,7 +155,7 @@ public class GitVcs extends AbstractVcs {
myRollbackEnvironment = gitRollbackEnvironment;
myRevSelector = new GitRevisionSelector();
myConfigurable = new GitVcsConfigurable(myProject, gitProjectSettings, sharedSettings);
- myUpdateEnvironment = new GitUpdateEnvironment(myProject, this, gitProjectSettings);
+ myUpdateEnvironment = new GitUpdateEnvironment(myProject, gitProjectSettings);
myCommittedChangeListProvider = new GitCommittedChangeListProvider(myProject);
myOutgoingChangesProvider = new GitOutgoingChangesProvider(myProject);
myTreeDiffProvider = new GitTreeDiffProvider(myProject);
diff --git a/plugins/git4idea/src/git4idea/push/GitPushOperation.java b/plugins/git4idea/src/git4idea/push/GitPushOperation.java
index f67a807bfbaf..8973cd2b7b4b 100644
--- a/plugins/git4idea/src/git4idea/push/GitPushOperation.java
+++ b/plugins/git4idea/src/git4idea/push/GitPushOperation.java
@@ -52,6 +52,7 @@ import git4idea.repo.GitRepositoryManager;
import git4idea.update.GitRebaseOverMergeProblem;
import git4idea.update.GitUpdateProcess;
import git4idea.update.GitUpdateResult;
+import git4idea.update.GitUpdater;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -383,7 +384,8 @@ public class GitPushOperation {
private void savePushUpdateSettings(@NotNull PushUpdateSettings settings, boolean rebaseOverMergeDetected) {
UpdateMethod updateMethod = settings.getUpdateMethod();
mySettings.setUpdateAllRootsIfPushRejected(settings.shouldUpdateAllRoots());
- if (!rebaseOverMergeDetected) { // don't overwrite explicit "rebase" with temporary "merge" caused by merge commits
+ if (!rebaseOverMergeDetected // don't overwrite explicit "rebase" with temporary "merge" caused by merge commits
+ && mySettings.getUpdateType() != updateMethod && mySettings.getUpdateType() != UpdateMethod.BRANCH_DEFAULT) { // don't overwrite "branch default" setting
mySettings.setUpdateType(updateMethod);
}
}
@@ -392,6 +394,10 @@ public class GitPushOperation {
private PushUpdateSettings readPushUpdateSettings() {
boolean updateAllRoots = mySettings.shouldUpdateAllRootsIfPushRejected();
UpdateMethod updateMethod = mySettings.getUpdateType();
+ if (updateMethod == UpdateMethod.BRANCH_DEFAULT) {
+ // deliberate limitation: we have only 2 buttons => choose method from the 1st repo if different
+ updateMethod = GitUpdater.resolveUpdateMethod(myProject, myPushSpecs.keySet().iterator().next().getRoot());
+ }
return new PushUpdateSettings(updateAllRoots, updateMethod);
}
@@ -429,12 +435,9 @@ public class GitPushOperation {
protected GitUpdateResult update(@NotNull Collection rootsToUpdate,
@NotNull UpdateMethod updateMethod,
boolean checkForRebaseOverMergeProblem) {
- GitUpdateProcess.UpdateMethod um = updateMethod == UpdateMethod.MERGE ?
- GitUpdateProcess.UpdateMethod.MERGE :
- GitUpdateProcess.UpdateMethod.REBASE;
GitUpdateResult updateResult = new GitUpdateProcess(myProject, myPlatformFacade, myProgressIndicator,
new HashSet(rootsToUpdate), UpdatedFiles.create(),
- checkForRebaseOverMergeProblem).update(um);
+ checkForRebaseOverMergeProblem).update(updateMethod);
for (GitRepository repository : rootsToUpdate) {
repository.getRoot().refresh(true, true);
repository.update();
diff --git a/plugins/git4idea/src/git4idea/push/GitRejectedPushUpdateDialog.java b/plugins/git4idea/src/git4idea/push/GitRejectedPushUpdateDialog.java
index fdbdf229d277..a24da32017ab 100644
--- a/plugins/git4idea/src/git4idea/push/GitRejectedPushUpdateDialog.java
+++ b/plugins/git4idea/src/git4idea/push/GitRejectedPushUpdateDialog.java
@@ -220,6 +220,12 @@ class GitRejectedPushUpdateDialog extends DialogWrapper {
return myRebaseOverMergeProblemDetected;
}
+ @TestOnly
+ @NotNull
+ Action getDefaultAction() {
+ return Boolean.TRUE.equals(myMergeAction.getValue(DEFAULT_ACTION)) ? myMergeAction : myRebaseAction;
+ }
+
private class MergeAction extends AbstractAction {
MergeAction() {
super("&Merge");
diff --git a/plugins/git4idea/src/git4idea/update/GitUpdateEnvironment.java b/plugins/git4idea/src/git4idea/update/GitUpdateEnvironment.java
index f5068f10383e..b7903cc2b719 100644
--- a/plugins/git4idea/src/git4idea/update/GitUpdateEnvironment.java
+++ b/plugins/git4idea/src/git4idea/update/GitUpdateEnvironment.java
@@ -16,7 +16,6 @@
package git4idea.update;
import com.intellij.openapi.components.ServiceManager;
-import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
@@ -29,7 +28,6 @@ import com.intellij.openapi.vcs.update.UpdateSession;
import com.intellij.openapi.vcs.update.UpdatedFiles;
import com.intellij.openapi.vfs.VirtualFile;
import git4idea.GitPlatformFacade;
-import git4idea.GitVcs;
import git4idea.config.GitVcsSettings;
import git4idea.repo.GitRepositoryManager;
import org.jetbrains.annotations.NotNull;
@@ -41,21 +39,12 @@ import java.util.Set;
import static git4idea.GitUtil.*;
-/**
- * Git update environment implementation. The environment does
- * {@code git pull -v} for each vcs root. Rebase variant is detected
- * and processed as well.
- */
public class GitUpdateEnvironment implements UpdateEnvironment {
- private final GitVcs myVcs;
private final Project myProject;
private final GitVcsSettings mySettings;
@NotNull private final GitPlatformFacade myPlatformFacade;
- private static final Logger LOG = Logger.getInstance(GitUpdateEnvironment.class);
-
- public GitUpdateEnvironment(@NotNull Project project, @NotNull GitVcs vcs, GitVcsSettings settings) {
- myVcs = vcs;
+ public GitUpdateEnvironment(@NotNull Project project, @NotNull GitVcsSettings settings) {
myProject = project;
mySettings = settings;
myPlatformFacade = ServiceManager.getService(project, GitPlatformFacade.class);
@@ -72,7 +61,7 @@ public class GitUpdateEnvironment implements UpdateEnvironment {
final GitUpdateProcess gitUpdateProcess = new GitUpdateProcess(myProject, myPlatformFacade,
progressIndicator, getRepositoriesFromRoots(repositoryManager, roots),
updatedFiles, true);
- boolean result = gitUpdateProcess.update(GitUpdateProcess.UpdateMethod.READ_FROM_SETTINGS).isSuccess();
+ boolean result = gitUpdateProcess.update(mySettings.getUpdateType()).isSuccess();
return new GitUpdateSession(result);
}
diff --git a/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java b/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java
index 33909cd9fa31..6c610575a00f 100644
--- a/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java
+++ b/plugins/git4idea/src/git4idea/update/GitUpdateProcess.java
@@ -45,6 +45,7 @@ import git4idea.GitUtil;
import git4idea.branch.GitBranchPair;
import git4idea.branch.GitBranchUtil;
import git4idea.commands.Git;
+import git4idea.config.UpdateMethod;
import git4idea.merge.GitConflictResolver;
import git4idea.merge.GitMergeCommittingConflictResolver;
import git4idea.merge.GitMerger;
@@ -83,12 +84,6 @@ public class GitUpdateProcess {
private GitUpdateResult myResult;
private final Collection myRootsToSave;
- public enum UpdateMethod {
- MERGE,
- REBASE,
- READ_FROM_SETTINGS
- }
-
public GitUpdateProcess(@NotNull Project project,
@NotNull GitPlatformFacade platformFacade,
@Nullable ProgressIndicator progressIndicator,
@@ -298,15 +293,8 @@ public class GitUpdateProcess {
LOG.info("updateImpl: defining updaters...");
for (GitRepository repository : myRepositories) {
VirtualFile root = repository.getRoot();
- final GitUpdater updater;
- if (updateMethod == UpdateMethod.MERGE) {
- updater = new GitMergeUpdater(myProject, myGit, root, myTrackedBranches, myProgressIndicator, myUpdatedFiles);
- } else if (updateMethod == UpdateMethod.REBASE) {
- updater = new GitRebaseUpdater(myProject, myGit, root, myTrackedBranches, myProgressIndicator, myUpdatedFiles);
- } else {
- updater = GitUpdater.getUpdater(myProject, myGit, myTrackedBranches, root, myProgressIndicator, myUpdatedFiles);
- }
-
+ GitUpdater updater = GitUpdater.getUpdater(myProject, myGit, myTrackedBranches, root, myProgressIndicator, myUpdatedFiles,
+ updateMethod);
if (updater.isUpdateNeeded()) {
updaters.put(root, updater);
}
diff --git a/plugins/git4idea/src/git4idea/update/GitUpdater.java b/plugins/git4idea/src/git4idea/update/GitUpdater.java
index a093aafba89b..42c44df2cf06 100644
--- a/plugins/git4idea/src/git4idea/update/GitUpdater.java
+++ b/plugins/git4idea/src/git4idea/update/GitUpdater.java
@@ -29,7 +29,7 @@ import git4idea.commands.Git;
import git4idea.commands.GitCommand;
import git4idea.commands.GitSimpleHandler;
import git4idea.config.GitConfigUtil;
-import git4idea.config.GitVcsSettings;
+import git4idea.config.UpdateMethod;
import git4idea.merge.MergeChangeCollector;
import git4idea.repo.GitRepositoryManager;
import org.jetbrains.annotations.NotNull;
@@ -76,43 +76,35 @@ public abstract class GitUpdater {
* @return {@link GitMergeUpdater} or {@link GitRebaseUpdater}.
*/
@NotNull
- public static GitUpdater getUpdater(@NotNull Project project, @NotNull Git git, @NotNull Map trackedBranches,
- @NotNull VirtualFile root, @NotNull ProgressIndicator progressIndicator,
- @NotNull UpdatedFiles updatedFiles) {
- final GitVcsSettings settings = GitVcsSettings.getInstance(project);
- if (settings == null) {
- return getDefaultUpdaterForBranch(project, git, root, trackedBranches, progressIndicator, updatedFiles);
+ public static GitUpdater getUpdater(@NotNull Project project,
+ @NotNull Git git,
+ @NotNull Map trackedBranches,
+ @NotNull VirtualFile root,
+ @NotNull ProgressIndicator progressIndicator,
+ @NotNull UpdatedFiles updatedFiles,
+ @NotNull UpdateMethod updateMethod) {
+ if (updateMethod == UpdateMethod.BRANCH_DEFAULT) {
+ updateMethod = resolveUpdateMethod(project, root);
}
- switch (settings.getUpdateType()) {
- case REBASE:
- return new GitRebaseUpdater(project, git, root, trackedBranches, progressIndicator, updatedFiles);
- case MERGE:
- return new GitMergeUpdater(project, git, root, trackedBranches, progressIndicator, updatedFiles);
- case BRANCH_DEFAULT:
- // use default for the branch
- return getDefaultUpdaterForBranch(project, git, root, trackedBranches, progressIndicator, updatedFiles);
- }
- return getDefaultUpdaterForBranch(project, git, root, trackedBranches, progressIndicator, updatedFiles);
+ return updateMethod == UpdateMethod.REBASE ?
+ new GitRebaseUpdater(project, git, root, trackedBranches, progressIndicator, updatedFiles):
+ new GitMergeUpdater(project, git, root, trackedBranches, progressIndicator, updatedFiles);
}
@NotNull
- private static GitUpdater getDefaultUpdaterForBranch(@NotNull Project project, @NotNull Git git, @NotNull VirtualFile root,
- @NotNull Map trackedBranches,
- @NotNull ProgressIndicator progressIndicator, @NotNull UpdatedFiles updatedFiles) {
- try {
- GitLocalBranch branch = GitBranchUtil.getCurrentBranch(project, root);
- boolean rebase = false;
- if (branch != null) {
+ public static UpdateMethod resolveUpdateMethod(@NotNull Project project, @NotNull VirtualFile root) {
+ GitLocalBranch branch = GitBranchUtil.getCurrentBranch(project, root);
+ boolean rebase = false;
+ if (branch != null) {
+ try {
String rebaseValue = GitConfigUtil.getValue(project, root, "branch." + branch.getName() + ".rebase");
rebase = rebaseValue != null && rebaseValue.equalsIgnoreCase("true");
}
- if (rebase) {
- return new GitRebaseUpdater(project, git, root, trackedBranches, progressIndicator, updatedFiles);
+ catch (VcsException e) {
+ LOG.warn("Couldn't get git config branch." + branch.getName() + ".rebase", e);
}
- } catch (VcsException e) {
- LOG.info("getDefaultUpdaterForBranch branch", e);
}
- return new GitMergeUpdater(project, git, root, trackedBranches, progressIndicator, updatedFiles);
+ return rebase ? UpdateMethod.REBASE : UpdateMethod.MERGE;
}
@NotNull
diff --git a/plugins/git4idea/tests/git4idea/push/GitPushOperationBaseTest.java b/plugins/git4idea/tests/git4idea/push/GitPushOperationBaseTest.java
index 4d6576ae6718..7328b4d305b9 100644
--- a/plugins/git4idea/tests/git4idea/push/GitPushOperationBaseTest.java
+++ b/plugins/git4idea/tests/git4idea/push/GitPushOperationBaseTest.java
@@ -18,7 +18,6 @@ package git4idea.push;
import com.intellij.dvcs.push.PushSpec;
import com.intellij.dvcs.push.PushSupport;
import com.intellij.openapi.extensions.Extensions;
-import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.vcs.AbstractVcsHelper;
@@ -148,9 +147,9 @@ abstract class GitPushOperationBaseTest extends GitPlatformTest {
}
protected void agreeToUpdate(final int exitCode) {
- myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler() {
+ myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler() {
@Override
- public int handleDialog(DialogWrapper dialog) {
+ public int handleDialog(GitRejectedPushUpdateDialog dialog) {
return exitCode;
}
});
diff --git a/plugins/git4idea/tests/git4idea/push/GitPushOperationSingleRepoTest.java b/plugins/git4idea/tests/git4idea/push/GitPushOperationSingleRepoTest.java
index 8f1882e09269..d5057f5d6683 100644
--- a/plugins/git4idea/tests/git4idea/push/GitPushOperationSingleRepoTest.java
+++ b/plugins/git4idea/tests/git4idea/push/GitPushOperationSingleRepoTest.java
@@ -18,6 +18,7 @@ package git4idea.push;
import com.intellij.dvcs.push.PushSpec;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
+import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.Trinity;
import com.intellij.openapi.util.io.FileUtil;
@@ -36,6 +37,7 @@ import git4idea.update.GitUpdateResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
@@ -47,6 +49,7 @@ import static git4idea.test.GitExecutor.*;
import static git4idea.test.GitTestUtil.makeCommit;
import static java.util.Collections.singletonMap;
+@SuppressWarnings("StringToUpperCaseOrToLowerCaseWithoutLocale")
public class GitPushOperationSingleRepoTest extends GitPushOperationBaseTest {
protected GitRepository myRepository;
@@ -121,9 +124,9 @@ public class GitPushOperationSingleRepoTest extends GitPushOperationBaseTest {
pushCommitFromBro();
final Ref dialogShown = Ref.create(false);
- myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler() {
+ myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler() {
@Override
- public int handleDialog(DialogWrapper dialog) {
+ public int handleDialog(GitRejectedPushUpdateDialog dialog) {
dialogShown.set(true);
return DialogWrapper.CANCEL_EXIT_CODE;
}
@@ -141,9 +144,9 @@ public class GitPushOperationSingleRepoTest extends GitPushOperationBaseTest {
git("checkout -b feature");
final Ref dialogShown = Ref.create(false);
- myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler() {
+ myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler() {
@Override
- public int handleDialog(DialogWrapper dialog) {
+ public int handleDialog(GitRejectedPushUpdateDialog dialog) {
dialogShown.set(true);
return DialogWrapper.CANCEL_EXIT_CODE;
}
@@ -188,6 +191,50 @@ public class GitPushOperationSingleRepoTest extends GitPushOperationBaseTest {
assertFalse("The commit shouldn't be pushed", history.contains(hash));
}
+ public void test_use_selected_update_method_for_all_consecutive_updates() throws IOException {
+ pushCommitFromBro();
+ cd(myRepository);
+ makeCommit("afile.txt");
+
+ agreeToUpdate(GitRejectedPushUpdateDialog.REBASE_EXIT_CODE);
+
+ refresh();
+ PushSpec pushSpec = makePushSpec(myRepository, "master", "origin/master");
+
+ GitPushResult result = new GitPushOperation(myProject, myPushSupport, singletonMap(myRepository, pushSpec), null, false) {
+ boolean updateHappened;
+
+ @NotNull
+ @Override
+ protected GitUpdateResult update(@NotNull Collection rootsToUpdate,
+ @NotNull UpdateMethod updateMethod,
+ boolean checkForRebaseOverMergeProblem) {
+ GitUpdateResult updateResult = super.update(rootsToUpdate, updateMethod, checkForRebaseOverMergeProblem);
+ try {
+ if (!updateHappened) {
+ updateHappened = true;
+ pushCommitFromBro();
+ }
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return updateResult;
+ }
+ }.execute();
+
+ assertResult(SUCCESS, 1, "master", "origin/master", GitUpdateResult.SUCCESS, result.getResults().get(myRepository));
+ cd(myRepository);
+ String[] commitMessages = StringUtil.splitByLines(log("--pretty=%s"));
+ boolean mergeCommitsInTheLog = ContainerUtil.exists(commitMessages, new Condition() {
+ @Override
+ public boolean value(String s) {
+ return s.toLowerCase().contains("merge");
+ }
+ });
+ assertFalse("Unexpected merge commits when rebase method is selected", mergeCommitsInTheLog);
+ }
+
public void test_force_push() throws IOException {
String lostHash = pushCommitFromBro();
cd(myRepository);
@@ -305,6 +352,64 @@ public class GitPushOperationSingleRepoTest extends GitPushOperationBaseTest {
UpdateMethod.REBASE, myGitSettings.getUpdateType());
}
+ public void test_respect_branch_default_setting_for_rejected_push_dialog() throws IOException {
+ generateUpdateNeeded();
+ myGitSettings.setUpdateType(UpdateMethod.BRANCH_DEFAULT);
+ git("config branch.master.rebase true");
+
+ final Ref defaultActionName = Ref.create();
+ myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler() {
+ @Override
+ public int handleDialog(@NotNull GitRejectedPushUpdateDialog dialog) {
+ defaultActionName.set((String)dialog.getDefaultAction().getValue(Action.NAME));
+ return DialogWrapper.CANCEL_EXIT_CODE;
+ }
+ });
+
+ push("master", "origin/master");
+ assertTrue("Default action in rejected-push dialog is incorrect: " + defaultActionName.get(),
+ defaultActionName.get().toLowerCase().contains("rebase"));
+
+ git("config branch.master.rebase false");
+ push("master", "origin/master");
+ assertTrue("Default action in rejected-push dialog is incorrect: " + defaultActionName.get(),
+ defaultActionName.get().toLowerCase().contains("merge"));
+ }
+
+ public void test_respect_branch_default_setting_for_silent_update_when_rejected_push() throws IOException {
+ generateUpdateNeeded();
+ myGitSettings.setUpdateType(UpdateMethod.BRANCH_DEFAULT);
+ git("config branch.master.rebase true");
+ myGitSettings.setAutoUpdateIfPushRejected(true);
+
+ push("master", "origin/master");
+ assertFalse("Unexpected merge commit: rebase should have happened", log("-1 --pretty=%s").toLowerCase().startsWith("merge"));
+ }
+
+ // there is no "branch default" choice in the rejected push dialog
+ // => simply don't rewrite the setting if the same value is chosen, as was default value initially
+ public void test_dont_overwrite_branch_default_setting_when_agree_in_rejected_push_dialog() throws IOException {
+ generateUpdateNeeded();
+ myGitSettings.setUpdateType(UpdateMethod.BRANCH_DEFAULT);
+ git("config branch.master.rebase true");
+
+ myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler() {
+ @Override
+ public int handleDialog(@NotNull GitRejectedPushUpdateDialog dialog) {
+ return GitRejectedPushUpdateDialog.REBASE_EXIT_CODE;
+ }
+ });
+
+ push("master", "origin/master");
+ assertEquals(UpdateMethod.BRANCH_DEFAULT, myGitSettings.getUpdateType());
+ }
+
+ private void generateUpdateNeeded() throws IOException {
+ pushCommitFromBro();
+ cd(myRepository);
+ makeCommit("file.txt");
+ }
+
private void generateUnpushedMergedCommitProblem() throws IOException {
pushCommitFromBro();
cd(myRepository);
diff --git a/plugins/git4idea/tests/git4idea/test/TestDialogManager.java b/plugins/git4idea/tests/git4idea/test/TestDialogManager.java
index d20904099d9f..871d53cab1fb 100644
--- a/plugins/git4idea/tests/git4idea/test/TestDialogManager.java
+++ b/plugins/git4idea/tests/git4idea/test/TestDialogManager.java
@@ -48,8 +48,15 @@ public class TestDialogManager extends DialogManager {
@Override
protected void showDialog(@NotNull DialogWrapper dialog) {
TestDialogHandler handler = myHandlers.get(dialog.getClass());
- int exitCode = handler != null ? handler.handleDialog(dialog) : DialogWrapper.OK_EXIT_CODE;
- dialog.close(exitCode, exitCode == DialogWrapper.OK_EXIT_CODE);
+ int exitCode = DialogWrapper.OK_EXIT_CODE;
+ try {
+ if (handler != null) {
+ exitCode = handler.handleDialog(dialog);
+ }
+ }
+ finally {
+ dialog.close(exitCode, exitCode == DialogWrapper.OK_EXIT_CODE);
+ }
}
@Override