mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<problems/>
|
||||
@@ -0,0 +1,13 @@
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.DoubleSupplier;
|
||||
|
||||
class B {
|
||||
|
||||
public void test(Consumer<DoubleSupplier> consumer) {
|
||||
consumer.accept(this::method);
|
||||
}
|
||||
|
||||
private double method() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
-12
@@ -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();
|
||||
}
|
||||
|
||||
+26
@@ -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;
|
||||
|
||||
-54
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<Integer, PsiReference>() {
|
||||
@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<Integer, PsiReference>() {
|
||||
@Override
|
||||
public PsiReference fun(Integer start) {
|
||||
return element.findReferenceAt(start);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiReference checkReferenceRange(PsiElement element, Function<Integer, PsiReference> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<Void> remove(@NotNull Breakpoint breakpoint);
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -60,6 +60,7 @@ public abstract class BreakpointManagerBase<T extends BreakpointBase<?>> impleme
|
||||
|
||||
protected abstract Promise<Breakpoint> 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<T extends BreakpointBase<?>> impleme
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Promise<Void> remove(@NotNull Breakpoint breakpoint) {
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -131,4 +133,10 @@ public abstract class BreakpointManagerBase<T extends BreakpointBase<?>> impleme
|
||||
public FunctionSupport getFunctionSupport() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ScriptRegExpSupport getScriptRegExpSupport() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+11
-1
@@ -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() {
|
||||
|
||||
@@ -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<String> classNamesIterator) {
|
||||
for (String className : classNamesIterator) {
|
||||
try {
|
||||
Class candidateClass = Class.forName(className);
|
||||
Class candidateClass = Class.forName(className, false, getClass().getClassLoader());
|
||||
addClassIfTestCase(candidateClass);
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
|
||||
@@ -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<T> 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<T> 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<T> 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<Integer> 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;
|
||||
|
||||
@@ -155,7 +155,7 @@ public class GitVcs extends AbstractVcs<CommittedChangeList> {
|
||||
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);
|
||||
|
||||
@@ -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<GitRepository> 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<GitRepository>(rootsToUpdate), UpdatedFiles.create(),
|
||||
checkForRebaseOverMergeProblem).update(um);
|
||||
checkForRebaseOverMergeProblem).update(updateMethod);
|
||||
for (GitRepository repository : rootsToUpdate) {
|
||||
repository.getRoot().refresh(true, true);
|
||||
repository.update();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<VirtualFile> 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);
|
||||
}
|
||||
|
||||
@@ -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<VirtualFile, GitBranchPair> 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<VirtualFile, GitBranchPair> 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<VirtualFile, GitBranchPair> 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
|
||||
|
||||
@@ -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<GitRejectedPushUpdateDialog>() {
|
||||
@Override
|
||||
public int handleDialog(DialogWrapper dialog) {
|
||||
public int handleDialog(GitRejectedPushUpdateDialog dialog) {
|
||||
return exitCode;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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<Boolean> dialogShown = Ref.create(false);
|
||||
myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler() {
|
||||
myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler<GitRejectedPushUpdateDialog>() {
|
||||
@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<Boolean> dialogShown = Ref.create(false);
|
||||
myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler() {
|
||||
myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler<GitRejectedPushUpdateDialog>() {
|
||||
@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<GitPushSource, GitPushTarget> 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<GitRepository> 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<String>() {
|
||||
@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<String> defaultActionName = Ref.create();
|
||||
myDialogManager.registerDialogHandler(GitRejectedPushUpdateDialog.class, new TestDialogHandler<GitRejectedPushUpdateDialog>() {
|
||||
@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<GitRejectedPushUpdateDialog>() {
|
||||
@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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user