diff --git a/platform/lang-impl/src/com/intellij/application/options/InitialConfigurationDialog.java b/platform/lang-impl/src/com/intellij/application/options/InitialConfigurationDialog.java index f2fcb2efe3ea..27625060354f 100644 --- a/platform/lang-impl/src/com/intellij/application/options/InitialConfigurationDialog.java +++ b/platform/lang-impl/src/com/intellij/application/options/InitialConfigurationDialog.java @@ -229,6 +229,11 @@ public class InitialConfigurationDialog extends DialogWrapper { @Override public void dataChanged() {} + @Override + public boolean isStillValid(Object o) { + return false; + } + @Override public void refresh() { updateColorSchemePreview(false); diff --git a/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java b/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java index c0758be537f4..922b3dd77b0a 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/PanelWithActionsAndCloseButton.java @@ -31,21 +31,31 @@ import java.awt.*; public abstract class PanelWithActionsAndCloseButton extends JPanel implements DataProvider { protected final ContentManager myContentManager; private final String myHelpId; + private final boolean myVerticalToolbar; + private boolean myCloseEnabled; private final DefaultActionGroup myToolbarGroup = new DefaultActionGroup(null, false); - public PanelWithActionsAndCloseButton(@NotNull ContentManager contentManager, @NonNls String helpId) { + public PanelWithActionsAndCloseButton(ContentManager contentManager, @NonNls String helpId) { + this(contentManager, helpId, true); + } + + public PanelWithActionsAndCloseButton(ContentManager contentManager, @NonNls String helpId, final boolean verticalToolbar) { super(new BorderLayout()); myContentManager = contentManager; myHelpId = helpId; + myVerticalToolbar = verticalToolbar; + myCloseEnabled = true; - myContentManager.addContentManagerListener(new ContentManagerAdapter(){ - public void contentRemoved(ContentManagerEvent event) { - if (event.getContent().getComponent() == PanelWithActionsAndCloseButton.this) { - dispose(); - myContentManager.removeContentManagerListener(this); + if (myContentManager != null) { + myContentManager.addContentManagerListener(new ContentManagerAdapter(){ + public void contentRemoved(ContentManagerEvent event) { + if (event.getContent().getComponent() == PanelWithActionsAndCloseButton.this) { + dispose(); + myContentManager.removeContentManagerListener(this); + } } - } - }); + }); + } } @@ -53,18 +63,26 @@ public abstract class PanelWithActionsAndCloseButton extends JPanel implements D return myHelpId; } + protected void disableClose() { + myCloseEnabled = false; + } + protected void init(){ addActionsTo(myToolbarGroup); myToolbarGroup.add(new MyCloseAction()); myToolbarGroup.add(ActionManager.getInstance().getAction(IdeActions.ACTION_CONTEXT_HELP)); - ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.FILEHISTORY_VIEW_TOOLBAR, myToolbarGroup, false); + ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.FILEHISTORY_VIEW_TOOLBAR, myToolbarGroup, ! myVerticalToolbar); JComponent centerPanel = createCenterPanel(); toolbar.setTargetComponent(centerPanel); - add(toolbar.getComponent(), BorderLayout.WEST); add(centerPanel, BorderLayout.CENTER); + if (myVerticalToolbar) { + add(toolbar.getComponent(), BorderLayout.WEST); + } else { + add(toolbar.getComponent(), BorderLayout.NORTH); + } } public Object getData(String dataId) { @@ -81,11 +99,18 @@ public abstract class PanelWithActionsAndCloseButton extends JPanel implements D protected void dispose() {} private class MyCloseAction extends CloseTabToolbarAction { + @Override + public void update(AnActionEvent e) { + super.update(e); + e.getPresentation().setVisible(myCloseEnabled); + } public void actionPerformed(AnActionEvent e) { - Content content = myContentManager.getContent(PanelWithActionsAndCloseButton.this); - if (content != null) { - myContentManager.removeContent(content, true); + if (myContentManager != null) { + Content content = myContentManager.getContent(PanelWithActionsAndCloseButton.this); + if (content != null) { + myContentManager.removeContent(content, true); + } } } } diff --git a/platform/platform-api/src/com/intellij/ui/dualView/DualView.java b/platform/platform-api/src/com/intellij/ui/dualView/DualView.java index 2e43a2ee903d..f1abf56b08ad 100644 --- a/platform/platform-api/src/com/intellij/ui/dualView/DualView.java +++ b/platform/platform-api/src/com/intellij/ui/dualView/DualView.java @@ -73,6 +73,8 @@ public class DualView extends JPanel { private final Storage.PropertiesComponentStorage myTreeStorage; private final PropertyChangeListener myPropertyChangeListener; + private boolean myZipByHeight; + public DualView(Object root, DualViewColumnInfo[] columns, @NonNls String columnServiceKey, Project project) { super(new CardLayout()); @@ -484,4 +486,22 @@ public class DualView extends JPanel { return result; } } + + @Override + public Dimension getPreferredSize() { + final Dimension was = super.getPreferredSize(); + if (! myZipByHeight) return was; + final int tableHeight = myFlatView.getTableHeader().getHeight() + myFlatView.getTableViewModel().getRowCount() * + myFlatView.getRowHeight(); + return new Dimension(was.width, tableHeight); + } + + @Override + public Dimension getMinimumSize() { + return myZipByHeight ? getPreferredSize() : super.getMinimumSize(); + } + + public void setZipByHeight(boolean zipByHeight) { + myZipByHeight = zipByHeight; + } } diff --git a/platform/platform-api/src/com/intellij/ui/table/TableView.java b/platform/platform-api/src/com/intellij/ui/table/TableView.java index 9d79f0a9e96a..3bc11c1f33c4 100644 --- a/platform/platform-api/src/com/intellij/ui/table/TableView.java +++ b/platform/platform-api/src/com/intellij/ui/table/TableView.java @@ -113,11 +113,12 @@ public class TableView extends BaseTableView implements ItemsProvider, Sel column.setPreferredWidth(width); column.setMinWidth(width); } - else if ((maxStringValue = columnInfo.getMaxStringValue()) != null) { + else if ((maxStringValue = columnInfo.getMaxStringValue(this)) != null) { int width = getFontMetrics(getFont()).stringWidth(maxStringValue) + columnInfo.getAdditionalWidth(); width = Math.max(width, headerSize.width); column.setPreferredWidth(width); column.setMaxWidth(width); + column.setMinWidth(width); } else if ((preferredValue = columnInfo.getPreferredStringValue()) != null) { int width = getFontMetrics(getFont()).stringWidth(preferredValue) + columnInfo.getAdditionalWidth(); diff --git a/platform/platform-impl/src/com/intellij/openapi/vcs/changes/RefreshablePanel.java b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/RefreshablePanel.java index 988173abfe8b..a7d7b0cb6f02 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vcs/changes/RefreshablePanel.java +++ b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/RefreshablePanel.java @@ -9,10 +9,11 @@ import javax.swing.*; * Date: 8/12/11 * Time: 6:47 PM */ -public interface RefreshablePanel extends Disposable { +public interface RefreshablePanel extends Disposable { boolean refreshDataSynch(); void dataChanged(); void refresh(); JPanel getPanel(); void away(); + boolean isStillValid(Data data); } diff --git a/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TabbedRefreshablePanel.java b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TabbedRefreshablePanel.java new file mode 100644 index 000000000000..8e06995daac2 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/openapi/vcs/changes/TabbedRefreshablePanel.java @@ -0,0 +1,97 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.changes; + +import com.intellij.openapi.util.Disposer; +import com.intellij.ui.components.JBTabbedPane; + +import javax.swing.*; +import java.awt.*; +import java.util.ArrayList; +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 4/25/12 + * Time: 4:53 PM + */ +public class TabbedRefreshablePanel implements RefreshablePanel { + private final JBTabbedPane myPane; + private final JPanel myPanel; + private final List myPanels; + + public TabbedRefreshablePanel() { + myPanels = new ArrayList(); + myPane = new JBTabbedPane(); + myPanel = new JPanel(new BorderLayout()); + myPanel.add(myPane, BorderLayout.CENTER); + } + + public void addTab(final String title, final RefreshablePanel panel) { + myPanels.add(panel); + myPane.add(title, panel.getPanel()); + } + + @Override + public boolean refreshDataSynch() { + for (RefreshablePanel panel : myPanels) { + if (! panel.refreshDataSynch()) return false; + } + return true; + } + + @Override + public boolean isStillValid(Object o) { + for (RefreshablePanel panel : myPanels) { + if (! panel.isStillValid(o)) return false; + } + return true; + } + + @Override + public void dataChanged() { + for (RefreshablePanel panel : myPanels) { + panel.dataChanged(); + } + } + + @Override + public void refresh() { + for (RefreshablePanel panel : myPanels) { + panel.refresh(); + } + } + + @Override + public JPanel getPanel() { + return myPanel; + } + + @Override + public void away() { + for (RefreshablePanel panel : myPanels) { + panel.away(); + } + } + + @Override + public void dispose() { + for (RefreshablePanel panel : myPanels) { + Disposer.dispose(panel); + } + } +} diff --git a/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml b/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml index d3216171a9a0..9bf934d49f88 100644 --- a/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/VcsExtensionPoints.xml @@ -42,6 +42,7 @@ interface="com.intellij.openapi.vcs.actions.VcsQuickListContentProvider"/> + \ No newline at end of file diff --git a/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java b/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java index 349ba180e964..048fd7c1728c 100644 --- a/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java @@ -39,6 +39,9 @@ import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; import com.intellij.testFramework.fixtures.TestFixtureBuilder; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.Processor; +import com.intellij.util.containers.Convertor; +import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.Nullable; import org.junit.Assert; @@ -62,57 +65,8 @@ public abstract class AbstractVcsTestCase { protected IdeaProjectTestFixture myProjectFixture; protected boolean myInitChangeListManager = true; - protected ProcessOutput runClient(String exeName, @Nullable String stdin, @Nullable final File workingDir, String[] commandLine) throws IOException { - final List arguments = new ArrayList(); - final File client = new File(myClientBinaryPath, SystemInfo.isWindows ? exeName + ".exe" : exeName); - if (client.exists()) { - arguments.add(client.toString()); - } - else { - // assume client is in path - arguments.add(exeName); - } - Collections.addAll(arguments, commandLine); - if (myTraceClient) { - System.out.println("*** running:\n" + arguments); - if (StringUtil.isNotEmpty(stdin)) { - System.out.println("*** stdin:\n" + stdin); - } - } - final ProcessBuilder builder = new ProcessBuilder().command(arguments); - if (workingDir != null) { - builder.directory(workingDir); - } - Process clientProcess = builder.start(); - - if (stdin != null) { - OutputStream outputStream = clientProcess.getOutputStream(); - try { - byte[] bytes = stdin.getBytes(); - outputStream.write(bytes); - } - finally { - outputStream.close(); - } - } - - CapturingProcessHandler handler = new CapturingProcessHandler(clientProcess, CharsetToolkit.getDefaultSystemCharset()); - ProcessOutput result = handler.runProcess(60*1000); - if (myTraceClient || result.isTimeout()) { - System.out.println("*** result: " + result.getExitCode()); - final String out = result.getStdout().trim(); - if (out.length() > 0) { - System.out.println("*** output:\n" + out); - } - final String err = result.getStderr().trim(); - if (err.length() > 0) { - System.out.println("*** error:\n" + err); - } - } - if (result.isTimeout()) { - throw new RuntimeException("Timeout waiting for VCS client to finish execution"); - } - return result; + protected TestClientRunner createClientRunner() { + return new TestClientRunner(myTraceClient, myClientBinaryPath); } public void setVcsMappings(VcsDirectoryMapping... mappings) { @@ -221,6 +175,25 @@ public abstract class AbstractVcsTestCase { return result.get(); } + protected void clearDirInCommand(final VirtualFile dir, final Processor filter) { + new WriteCommandAction.Simple(myProject) { + @Override + protected void run() throws Throwable { + try { + final VirtualFile[] children = dir.getChildren(); + for (VirtualFile child : children) { + if (filter != null && filter.process(child)) { + child.delete(null); + } + } + } + catch (IOException e) { + throw new RuntimeException(e); + } + } + }.execute(); + } + protected void tearDownProject() throws Exception { if (myProject != null) { ((ProjectComponent) VcsDirtyScopeManager.getInstance(myProject)).projectClosed(); diff --git a/platform/testFramework/src/com/intellij/testFramework/TestClientRunner.java b/platform/testFramework/src/com/intellij/testFramework/TestClientRunner.java new file mode 100644 index 000000000000..44f0d5be8161 --- /dev/null +++ b/platform/testFramework/src/com/intellij/testFramework/TestClientRunner.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.testFramework; + +import com.intellij.execution.process.CapturingProcessHandler; +import com.intellij.execution.process.ProcessOutput; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.CharsetToolkit; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 5/2/12 + * Time: 4:51 PM + */ +public class TestClientRunner { + protected boolean myTraceClient = false; + protected File myClientBinaryPath; + + public TestClientRunner(boolean traceClient, File clientBinaryPath) { + myTraceClient = traceClient; + myClientBinaryPath = clientBinaryPath; + } + + public ProcessOutput runClient(String exeName, @Nullable String stdin, @Nullable final File workingDir, String[] commandLine) throws + IOException { + final List arguments = new ArrayList(); + final File client = new File(myClientBinaryPath, SystemInfo.isWindows ? exeName + ".exe" : exeName); + if (client.exists()) { + arguments.add(client.toString()); + } + else { + // assume client is in path + arguments.add(exeName); + } + Collections.addAll(arguments, commandLine); + if (myTraceClient) { + System.out.println("*** running:\n" + arguments); + if (StringUtil.isNotEmpty(stdin)) { + System.out.println("*** stdin:\n" + stdin); + } + } + final ProcessBuilder builder = new ProcessBuilder().command(arguments); + if (workingDir != null) { + builder.directory(workingDir); + } + Process clientProcess = builder.start(); + + if (stdin != null) { + OutputStream outputStream = clientProcess.getOutputStream(); + try { + byte[] bytes = stdin.getBytes(); + outputStream.write(bytes); + } + finally { + outputStream.close(); + } + } + + CapturingProcessHandler handler = new CapturingProcessHandler(clientProcess, CharsetToolkit.getDefaultSystemCharset()); + ProcessOutput result = handler.runProcess(60*1000); + if (myTraceClient || result.isTimeout()) { + System.out.println("*** result: " + result.getExitCode()); + final String out = result.getStdout().trim(); + if (out.length() > 0) { + System.out.println("*** output:\n" + out); + } + final String err = result.getStderr().trim(); + if (err.length() > 0) { + System.out.println("*** error:\n" + err); + } + } + if (result.isTimeout()) { + throw new RuntimeException("Timeout waiting for VCS client to finish execution"); + } + return result; + } +} diff --git a/platform/util/src/com/intellij/util/ui/ColumnInfo.java b/platform/util/src/com/intellij/util/ui/ColumnInfo.java index 98add315824d..579fdd644609 100644 --- a/platform/util/src/com/intellij/util/ui/ColumnInfo.java +++ b/platform/util/src/com/intellij/util/ui/ColumnInfo.java @@ -93,7 +93,7 @@ public abstract class ColumnInfo { } @Nullable - public String getMaxStringValue() { + public String getMaxStringValue(JTable table) { return null; } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/GenericDetailsLoader.java b/platform/vcs-api/src/com/intellij/openapi/vcs/GenericDetailsLoader.java index 94aa92436d7e..ac04a647db82 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/GenericDetailsLoader.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/GenericDetailsLoader.java @@ -69,6 +69,10 @@ public class GenericDetailsLoader implements Details, Disposa myValueConsumer.consume(id, data); } + public void resetValueConsumer() { + myValueConsumer.reset(); + } + @CalledInAny @Override public Id getCurrentlySelected() { diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/ValueConsumer.java b/platform/vcs-api/src/com/intellij/openapi/vcs/ValueConsumer.java index b6f6283c07c0..83c9c9b67ad7 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/ValueConsumer.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/ValueConsumer.java @@ -51,4 +51,8 @@ public class ValueConsumer { myId = id; mySetId = null; } + + public void reset() { + mySetId = null; + } } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java index cfa29b4b42b8..629efb096a35 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/VcsConfiguration.java @@ -89,6 +89,7 @@ public final class VcsConfiguration implements PersistentStateComponent public Boolean SHOW_PATCH_IN_EXPLORER = null; public boolean SHOW_FILE_HISTORY_DETAILS = true; public boolean SHOW_VCS_ERROR_NOTIFICATIONS = true; + public boolean CHANGE_DETAILS_ON = false; public enum StandardOption { ADD(VcsBundle.message("vcs.command.name.add")), diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/Change.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/Change.java index ff52469a6783..1e42c92898f0 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/Change.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/Change.java @@ -95,6 +95,10 @@ public class Change { return myOtherLayers; } + public boolean isTreeConflict() { + return false; + } + public boolean hasOtherLayers() { return ! myOtherLayers.isEmpty(); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/SelectFilesToAddTextsToPatchPanel.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/SelectFilesToAddTextsToPatchPanel.java index 3d1a339c74f6..d144181c8301 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/SelectFilesToAddTextsToPatchPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/SelectFilesToAddTextsToPatchPanel.java @@ -158,6 +158,11 @@ public class SelectFilesToAddTextsToPatchPanel implements RefreshablePanel { return false; } + @Override + public boolean isStillValid(Object o) { + return false; + } + @Override public JPanel getPanel() { if (myPanel == null) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java index 712550235c29..5bd033318867 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PatchApplier.java @@ -91,6 +91,10 @@ public class PatchApplier { }); } + public void setIgnoreContentRootsCheck() { + myVerifier.setIgnoreContentRootsCheck(true); + } + public PatchApplier(final Project project, final VirtualFile baseDirectory, final List patches, final LocalChangeList targetChangeList, final CustomBinaryPatchApplier customForBinaries, final CommitContext commitContext) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java index 56c13f48e643..39b73342df26 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java +++ b/platform/vcs-impl/src/com/intellij/openapi/diff/impl/patch/formove/PathsVerifier.java @@ -55,6 +55,7 @@ public class PathsVerifier { private DelayedPrecheckContext myDelayedPrecheckContext; private List myAddedPaths; private List myDeletedPaths; + private boolean myIgnoreContentRootsCheck; public PathsVerifier(final Project project, final VirtualFile baseDirectory, final List patches, BaseMapper baseMapper) { myProject = project; @@ -314,6 +315,7 @@ public class PathsVerifier { } protected boolean checkModificationValid(final VirtualFile file, final String name) { + if (ApplicationManager.getApplication().isUnitTestMode() && myIgnoreContentRootsCheck) return true; // security check to avoid overwriting system files with a patch if ((file == null) || (!inContent(file)) || (myVcsManager.getVcsRootFor(file) == null)) { setErrorMessage("File to patch found outside content root: " + name); @@ -587,4 +589,8 @@ public class PathsVerifier { return mySkipDeleted.keySet(); } } + + public void setIgnoreContentRootsCheck(boolean ignoreContentRootsCheck) { + myIgnoreContentRootsCheck = ignoreContentRootsCheck; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/AbstractRefreshablePanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/AbstractRefreshablePanel.java index 21f7954a3e37..070a4e95587a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/AbstractRefreshablePanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/AbstractRefreshablePanel.java @@ -43,7 +43,7 @@ import javax.swing.*; * Date: 9/7/11 * Time: 3:13 PM */ -public abstract class AbstractRefreshablePanel implements RefreshablePanel { +public abstract class AbstractRefreshablePanel implements RefreshablePanel { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.AbstractRefreshablePanel"); protected final Ticket myTicket; private final DetailsPanel myDetailsPanel; @@ -124,6 +124,11 @@ public abstract class AbstractRefreshablePanel implements RefreshablePanel { return myDetailsPanel.getPanel(); } + @Override + public boolean isStillValid(Change data) { + return true; + } + private class Loader extends ModalityIgnorantBackgroundableTask { private final Ticket myTicketCopy; private T myT; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java index f985a77fb01d..b65020e3e352 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/FragmentedDiffRequestFromChange.java @@ -59,7 +59,7 @@ public class FragmentedDiffRequestFromChange { } public static boolean canCreateRequest(Change change) { - if (ChangesUtil.isTextConflictingChange(change)) return false; + if (ChangesUtil.isTextConflictingChange(change) || change.isTreeConflict()) return false; if (ShowDiffAction.isBinaryChange(change)) return false; final FilePath filePath = ChangesUtil.getFilePath(change); if (filePath.isDirectory()) return false; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ShortDiffDetails.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ShortDiffDetails.java index 5c30d2039e35..718558b37972 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ShortDiffDetails.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ShortDiffDetails.java @@ -38,7 +38,7 @@ import javax.swing.*; * Date: 8/17/11 * Time: 7:08 PM */ -public class ShortDiffDetails implements RefreshablePanel, Disposable { +public class ShortDiffDetails implements RefreshablePanel, Disposable { private final Project myProject; private final VcsChangeDetailsManager myVcsChangeDetailsManager; private final Getter myMaster; @@ -66,6 +66,11 @@ public class ShortDiffDetails implements RefreshablePanel, Disposable { }; } + @Override + public boolean isStillValid(Change change) { + return true; + } + @Override public boolean refreshDataSynch() { Change selected = myDetailsLoader.getCurrentlySelected(); @@ -150,7 +155,8 @@ public class ShortDiffDetails implements RefreshablePanel, Disposable { myDetailsCache.put(filePath, pair); } else if (old != pair) { if (pair != null) { - Disposer.dispose(pair); + myDetailsCache.put(filePath, pair); + Disposer.dispose(old); } } } @@ -174,6 +180,11 @@ public class ShortDiffDetails implements RefreshablePanel, Disposable { final FilePath filePath = ChangesUtil.getFilePath(change); RefreshablePanel details = myDetailsCache.get(filePath); + if (details != null && ! details.isStillValid(change)) { + Disposer.dispose(details); + details = null; + myDetailsLoader.resetValueConsumer(); + } if (details != null) { myDetailsConsumer.consume(change, details); } else { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsChangeDetailsManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsChangeDetailsManager.java index f598d61ca0f5..b33b94e1ce00 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsChangeDetailsManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsChangeDetailsManager.java @@ -24,9 +24,11 @@ import com.intellij.openapi.diff.DiffPanel; import com.intellij.openapi.diff.ex.DiffPanelEx; import com.intellij.openapi.diff.ex.DiffPanelOptions; import com.intellij.openapi.diff.impl.DiffPanelImpl; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.progress.BackgroundTaskQueue; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsKey; @@ -41,6 +43,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -51,7 +54,7 @@ import java.util.Map; * Time: 5:36 PM */ public class VcsChangeDetailsManager { - private final Map myProviderMap = new HashMap(); + private final List myProviders = new ArrayList(); private final List myDedicatedList; private final Project myProject; private final BackgroundTaskQueue myQueue; @@ -61,8 +64,11 @@ public class VcsChangeDetailsManager { myQueue = new BackgroundTaskQueue(myProject, "Loading change details"); myDedicatedList = new ArrayList(); - myDedicatedList.add(new BinaryDetailsProviderNew(project, myQueue)); - myDedicatedList.add(new FragmentedDiffDetailsProvider(myProject, myQueue)); + myDedicatedList.add(new BinaryDetailsProviderNew(project)); + myDedicatedList.add(new FragmentedDiffDetailsProvider(myProject)); + + VcsChangeDetailsProvider[] extensions = Extensions.getExtensions(VcsChangeDetailsProvider.EP_NAME, myProject); + myProviders.addAll(Arrays.asList(extensions)); Disposer.register(project, new Disposable() { @Override @@ -76,16 +82,40 @@ public class VcsChangeDetailsManager { for (VcsChangeDetailsProvider provider : myDedicatedList) { if (provider.canComment(change)) return true; } + for (VcsChangeDetailsProvider provider : myProviders) { + if (provider.canComment(change)) return true; + } return false; } @Nullable public RefreshablePanel getPanel(final Change change, JComponent parent) { + final List> panels = new ArrayList>(); for (VcsChangeDetailsProvider convertor : myDedicatedList) { if (! convertor.canComment(change)) continue; - RefreshablePanel panel = convertor.comment(change, parent); + RefreshablePanel panel = convertor.comment(change, parent, myQueue); if (panel != null) { - return panel; + panels.add(new Pair("Diff", panel)); + break; // only one of dedicated for now + } + } + for (VcsChangeDetailsProvider provider : myProviders) { + if (provider.canComment(change)) { + RefreshablePanel panel = provider.comment(change, parent, myQueue); + if (panel != null) { + panels.add(new Pair(provider.getName(), panel)); + } + } + } + if (! panels.isEmpty()) { + if (panels.size() == 1) { + return panels.get(0).getSecond(); + } else { + TabbedRefreshablePanel tabbedRefreshablePanel = new TabbedRefreshablePanel(); + for (Pair panel : panels) { + tabbedRefreshablePanel.addTab(panel.getFirst(), panel.getSecond()); + } + return tabbedRefreshablePanel; } } return null; @@ -97,11 +127,9 @@ public class VcsChangeDetailsManager { private static class BinaryDetailsProviderNew implements VcsChangeDetailsProvider { private final Project myProject; - private final BackgroundTaskQueue myQueue; - private BinaryDetailsProviderNew(Project project, final BackgroundTaskQueue queue) { + private BinaryDetailsProviderNew(Project project) { myProject = project; - myQueue = queue; } @Override @@ -111,17 +139,22 @@ public class VcsChangeDetailsManager { @Override public boolean canComment(Change change) { - FilePath path = ChangesUtil.getFilePath(change); - if (path != null && path.isDirectory()) return false; - return ShowDiffAction.isBinaryChangeAndCanShow(myProject, change); + return canBeShownInBinaryDiff(change, myProject); } @Override - public RefreshablePanel comment(Change change, JComponent parent) { - return new BinaryDiffDetailsPanel(myProject, myQueue, change); + public RefreshablePanel comment(Change change, JComponent parent, BackgroundTaskQueue queue) { + return new BinaryDiffDetailsPanel(myProject, queue, change); } } + private static boolean canBeShownInBinaryDiff(Change change, final Project project) { + FilePath path = ChangesUtil.getFilePath(change); + if (path != null && path.isDirectory()) return false; + if (change.isTreeConflict()) return false; + return ShowDiffAction.isBinaryChangeAndCanShow(project, change); + } + private static class BinaryDiffDetailsPanel extends AbstractRefreshablePanel>>> { private final BinaryDiffRequestFromChange myRequestFromChange; private final Project myProject; @@ -144,6 +177,11 @@ public class VcsChangeDetailsManager { o.setRequestFocusOnNewContent(false); } + @Override + public boolean isStillValid(Change data) { + return canBeShownInBinaryDiff(data, myProject); + } + @Override protected void refreshPresentation() { } @@ -211,11 +249,9 @@ public class VcsChangeDetailsManager { private static class FragmentedDiffDetailsProvider implements VcsChangeDetailsProvider { private final Project myProject; - private final BackgroundTaskQueue myQueue; - private FragmentedDiffDetailsProvider(Project project, final BackgroundTaskQueue queue) { + private FragmentedDiffDetailsProvider(Project project) { myProject = project; - myQueue = queue; } @Override @@ -229,8 +265,8 @@ public class VcsChangeDetailsManager { } @Override - public RefreshablePanel comment(Change change, JComponent parent) { - return new FragmentedDiffDetailsPanel(myProject, myQueue, change, parent); + public RefreshablePanel comment(Change change, JComponent parent, BackgroundTaskQueue queue) { + return new FragmentedDiffDetailsPanel(myProject, queue, change, parent); } } @@ -256,6 +292,11 @@ public class VcsChangeDetailsManager { myDiffPanel.refreshPresentation(); } + @Override + public boolean isStillValid(Change data) { + return FragmentedDiffRequestFromChange.canCreateRequest(data); + } + @Override protected ValueWithVcsException loadImpl() throws VcsException { return new ValueWithVcsException() { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsChangeDetailsProvider.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsChangeDetailsProvider.java index 75da00932629..f1b4c96301b0 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsChangeDetailsProvider.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsChangeDetailsProvider.java @@ -17,6 +17,7 @@ package com.intellij.openapi.vcs.changes; import com.intellij.openapi.Disposable; import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.progress.BackgroundTaskQueue; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.CalledInAwt; import com.intellij.openapi.vcs.CalledInBackground; @@ -36,5 +37,5 @@ public interface VcsChangeDetailsProvider { @CalledInAwt boolean canComment(final Change change); @CalledInAwt - RefreshablePanel comment(final Change change, JComponent parent); + RefreshablePanel comment(final Change change, JComponent parent, BackgroundTaskQueue queue); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java index 7796ec38f6e7..dd921862163e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryPanelImpl.java @@ -72,10 +72,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableCellEditor; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableModel; +import javax.swing.table.*; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.TreeCellRenderer; import javax.swing.tree.TreePath; @@ -109,7 +106,8 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { private final AnnotationProvider myAnnotationProvider; private VcsHistorySession myHistorySession; private final FilePath myFilePath; - private final FileHistoryRefresher myRefresher; + private final FileHistoryRefresherI myRefresherI; + private VcsFileRevision myBottomRevisionForShowDiff; private final DualView myDualView; private final Alarm myUpdateAlarm; @@ -122,6 +120,7 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { @NonNls private static final String VCS_HISTORY_ACTIONS_GROUP = "VcsHistoryActionsGroup"; private final Map myRevisionsOrder; + private boolean myIsStaticAndEmbedded; private final Comparator myRevisionsInOrderComparator = new Comparator() { @Override @@ -151,6 +150,11 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { public String getPreferredStringValue() { return "123.4567"; } + + @Override + public String getMaxStringValue(JTable table) { + return getMaxValue(getName(), table); + } }; private static final DualViewColumnInfo DATE = new VcsColumnInfo(VcsBundle.message("column.name.revision.date")) { @@ -168,6 +172,11 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { public String getPreferredStringValue() { return DateFormatUtil.formatPrettyDateTime(Clock.getTime()); } + + @Override + public String getMaxStringValue(JTable table) { + return getMaxValue(getName(), table); + } }; private final Splitter myDetailsSplitter = new Splitter(false, 0.5f); @@ -254,6 +263,11 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { public String getPreferredStringValue() { return "author_author"; } + + @Override + public String getMaxStringValue(JTable table) { + return getMaxValue(getName(), table); + } }; private Splitter mySplitter; @@ -325,6 +339,11 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { public TableCellRenderer getRenderer(VcsFileRevision p0) { return myRenderer; } + + @Override + public String getMaxStringValue(JTable table) { + return getMaxValue(getName(), table); + } } private final Map myRevisionToVirtualFile = new HashMap(); @@ -332,12 +351,20 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { public FileHistoryPanelImpl(AbstractVcs vcs, FilePath filePath, VcsHistorySession session, VcsHistoryProvider provider, - ContentManager contentManager, final FileHistoryRefresher refresher) { - super(contentManager, provider.getHelpId() != null ? provider.getHelpId() : "reference.versionControl.toolwindow.history"); + ContentManager contentManager, final FileHistoryRefresherI refresherI) { + this(vcs, filePath, session, provider, contentManager, refresherI, false); + } + + public FileHistoryPanelImpl(AbstractVcs vcs, + FilePath filePath, VcsHistorySession session, + VcsHistoryProvider provider, + ContentManager contentManager, final FileHistoryRefresherI refresherI, final boolean isStaticEmbedded) { + super(contentManager, provider.getHelpId() != null ? provider.getHelpId() : "reference.versionControl.toolwindow.history", ! isStaticEmbedded); + myIsStaticAndEmbedded = false; myVcs = vcs; myProvider = provider; myAnnotationProvider = myVcs.getCachingAnnotationProvider(); - myRefresher = refresher; + myRefresherI = refresherI; myHistorySession = session; myFilePath = filePath; @@ -373,6 +400,9 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { listener.install(myDualView.getTreeView()); createDualView(); + if (isStaticEmbedded) { + setIsStaticAndEmbedded(isStaticEmbedded); + } myPopupActions = createPopupActions(); @@ -521,6 +551,7 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { wrapWithTreeElements(myHistorySession.getRevisionList())), myTargetSelection); } + myDualView.getFlatView().updateColumnSizes(); myDualView.expandAll(); myDualView.repaint(); } @@ -689,7 +720,7 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { } private void setupDetails() { - boolean showDetails = getConfiguration().SHOW_FILE_HISTORY_DETAILS; + boolean showDetails = ! myIsStaticAndEmbedded && getConfiguration().SHOW_FILE_HISTORY_DETAILS; if (showDetails) { myDualView.setViewBorder(IdeBorderFactory.createBorder(SideBorder.LEFT | SideBorder.BOTTOM)); } @@ -717,7 +748,7 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { getConfiguration().FILE_HISTORY_SPLITTER_PROPORTION = newProportion.floatValue(); } - private float getSplitterProportion() { + protected float getSplitterProportion() { return getConfiguration().FILE_HISTORY_SPLITTER_PROPORTION; } @@ -779,18 +810,20 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { } } result.add(new RefreshFileHistoryAction()); - result.add(new ToggleAction("Show Details", "Display details panel", IconLoader.getIcon("/actions/showSource.png")) { - @Override - public boolean isSelected(AnActionEvent e) { - return getConfiguration().SHOW_FILE_HISTORY_DETAILS; - } + if (! myIsStaticAndEmbedded) { + result.add(new ToggleAction("Show Details", "Display details panel", IconLoader.getIcon("/actions/showSource.png")) { + @Override + public boolean isSelected(AnActionEvent e) { + return getConfiguration().SHOW_FILE_HISTORY_DETAILS; + } - @Override - public void setSelected(AnActionEvent e, boolean state) { - getConfiguration().SHOW_FILE_HISTORY_DETAILS = state; - setupDetails(); - } - }); + @Override + public void setSelected(AnActionEvent e, boolean state) { + getConfiguration().SHOW_FILE_HISTORY_DETAILS = state; + setupDetails(); + } + }); + } if (!popup && supportsTree()) { result.add(new MyShowAsTreeAction()); @@ -809,7 +842,8 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { mySplitter.revalidate(); mySplitter.repaint(); - myRefresher.run(true); + myRefresherI.run(true); + myDualView.getFlatView().updateColumnSizes(); } }.callMe(); } @@ -855,7 +889,8 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { final int selectedRow = flatView.getSelectedRow(); if (selectedRow == (flatView.getRowCount() - 1)) { // no previous - showDifferences(myVcs.getProject(), VcsFileRevision.NULL, getFirstSelectedRevision()); + showDifferences(myVcs.getProject(), myBottomRevisionForShowDiff != null ? myBottomRevisionForShowDiff : VcsFileRevision.NULL, + getFirstSelectedRevision()); } else { showDifferences(myVcs.getProject(), flatView.getRow(selectedRow + 1), getFirstSelectedRevision()); } @@ -1395,7 +1430,7 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { } } - protected void dispose() { + public void dispose() { super.dispose(); myDualView.dispose(); myUpdateAlarm.dispose(); @@ -1515,8 +1550,10 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { return myBaseColumn.getEditor(item.myRevision); } - public String getMaxStringValue() { - return myBaseColumn.getMaxStringValue(); + public String getMaxStringValue(JTable table) { + final String superValue = myBaseColumn.getMaxStringValue(table); + if (superValue != null) return superValue; + return getMaxValue(myBaseColumn.getName(), table); } public int getAdditionalWidth() { @@ -1557,6 +1594,34 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { return myFilePath.getVirtualFileParent(); } + private static String getMaxValue(String name, JTable table) { + if (table.getRowCount() == 0) return null; + final Enumeration columns = table.getColumnModel().getColumns(); + int idx = 0; + while (columns.hasMoreElements()) { + TableColumn column = columns.nextElement(); + if (name.equals(column.getHeaderValue())) { + break; + } + ++ idx; + } + if (idx >= table.getColumnModel().getColumnCount() - 1) return null; + final FontMetrics fm = table.getFontMetrics(table.getFont().deriveFont(Font.BOLD)); + final Object header = table.getColumnModel().getColumn(idx).getHeaderValue(); + double maxValue = fm.stringWidth((String)header); + String value = (String)header; + for (int i = 0; i < table.getRowCount(); i++) { + final Object at = table.getValueAt(i, idx); + if (at instanceof String) { + final int newWidth = fm.stringWidth((String)at); + if (newWidth > maxValue) { + maxValue = newWidth; + value = (String) at; + } + } + } + return value + "ww"; + } private class MyTreeCellRenderer implements TreeCellRenderer { private final TreeCellRenderer myDefaultCellRenderer; @@ -1650,4 +1715,21 @@ public class FileHistoryPanelImpl extends PanelWithActionsAndCloseButton { ++ cnt; } } + + public void setIsStaticAndEmbedded(boolean isStaticAndEmbedded) { + myIsStaticAndEmbedded = isStaticAndEmbedded; + myDualView.setZipByHeight(isStaticAndEmbedded); + myDualView.getFlatView().updateColumnSizes(); + if (myIsStaticAndEmbedded) { + disableClose(); + myDualView.getFlatView().getTableHeader().setBorder(IdeBorderFactory.createBorder(SideBorder.TOP)); + myDualView.getTreeView().getTableHeader().setBorder(IdeBorderFactory.createBorder(SideBorder.TOP)); + myDualView.getFlatView().setBorder(null); + myDualView.getTreeView().setBorder(null); + } + } + + public void setBottomRevisionForShowDiff(VcsFileRevision bottomRevisionForShowDiff) { + myBottomRevisionForShowDiff = bottomRevisionForShowDiff; + } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryRefresher.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryRefresher.java index e15c99a873d6..eb8048366a8b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryRefresher.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryRefresher.java @@ -24,7 +24,7 @@ import com.intellij.openapi.vcs.annotate.AnnotationProvider; * @author irengrig * @author Kirill Likhodedov */ -public class FileHistoryRefresher { +public class FileHistoryRefresher implements FileHistoryRefresherI { private final FileHistorySessionPartner mySessionPartner; private final VcsHistoryProvider myVcsHistoryProvider; private final FilePath myPath; @@ -47,6 +47,7 @@ public class FileHistoryRefresher { /** * @param refresh if true, than this is a refresh. If false, the history is shown for the first time. */ + @Override public void run(boolean isRefresh) { final VcsHistoryProviderBackgroundableProxy proxy = new VcsHistoryProviderBackgroundableProxy( myVcs, myVcsHistoryProvider, myVcs.getDiffProvider()); @@ -59,6 +60,7 @@ public class FileHistoryRefresher { * Was the refresher called for the first time or via refresh. * @return */ + @Override public boolean isFirstTime() { return !myIsRefresh; } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryRefresherI.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryRefresherI.java new file mode 100644 index 000000000000..3f113a9918ed --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistoryRefresherI.java @@ -0,0 +1,14 @@ +package com.intellij.openapi.vcs.history; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 4/27/12 + * Time: 11:47 AM + * To change this template use File | Settings | File Templates. + */ +public interface FileHistoryRefresherI { + void run(boolean isRefresh); + + boolean isFirstTime(); +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistorySessionPartner.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistorySessionPartner.java index 78eb8f2389e8..c5eb16d78ea8 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistorySessionPartner.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/history/FileHistorySessionPartner.java @@ -46,7 +46,7 @@ public class FileHistorySessionPartner implements VcsAppendableHistorySessionPar private final FilePath myPath; private final String myRepositoryPath; private final AbstractVcs myVcs; - private final FileHistoryRefresher myRefresher; + private final FileHistoryRefresherI myRefresherI; private volatile VcsAbstractHistorySession mySession; private final BufferedListConsumer myBuffer; @@ -54,13 +54,13 @@ public class FileHistorySessionPartner implements VcsAppendableHistorySessionPar final FilePath path, final String repositoryPath, final AbstractVcs vcs, - final FileHistoryRefresher refresher) { + final FileHistoryRefresherI refresherI) { myVcsHistoryProvider = vcsHistoryProvider; myAnnotationProvider = annotationProvider; myPath = path; myRepositoryPath = repositoryPath; myVcs = vcs; - myRefresher = refresher; + myRefresherI = refresherI; myBuffer = new BufferedListConsumer(5, new Consumer>() { public void consume(List vcsFileRevisions) { mySession.getRevisionList().addAll(vcsFileRevisions); @@ -83,7 +83,7 @@ public class FileHistorySessionPartner implements VcsAppendableHistorySessionPar ContentManager contentManager = ProjectLevelVcsManagerEx.getInstanceEx(myVcs.getProject()).getContentManager(); final VcsHistorySession copy = mySession.copyWithCachedRevision(); myFileHistoryPanel = new FileHistoryPanelImpl(myVcs, myPath, copy, myVcsHistoryProvider, - contentManager, myRefresher); + contentManager, myRefresherI); } return myFileHistoryPanel; } @@ -93,7 +93,7 @@ public class FileHistorySessionPartner implements VcsAppendableHistorySessionPar if (myFileHistoryPanel == null) { ContentManager contentManager = ProjectLevelVcsManagerEx.getInstanceEx(myVcs.getProject()).getContentManager(); myFileHistoryPanel = new FileHistoryPanelImpl(myVcs, myPath, copy, myVcsHistoryProvider, - contentManager, myRefresher); + contentManager, myRefresherI); } else { myFileHistoryPanel.getHistoryPanelRefresh().consume(copy); } @@ -117,7 +117,7 @@ public class FileHistorySessionPartner implements VcsAppendableHistorySessionPar ToolWindow toolWindow = ToolWindowManager.getInstance(myVcs.getProject()).getToolWindow(ToolWindowId.VCS); assert toolWindow != null : "Version Control ToolWindow should be available at this point."; - if (myRefresher.isFirstTime()) { + if (myRefresherI.isFirstTime()) { toolWindow.activate(null); } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java index 52c75a6a300e..ac91eac9666a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/AbstractVcsHelperImpl.java @@ -123,8 +123,8 @@ public class AbstractVcsHelperImpl extends AbstractVcsHelper { public void showFileHistory(final VcsHistoryProvider vcsHistoryProvider, final AnnotationProvider annotationProvider, final FilePath path, final String repositoryPath, final AbstractVcs vcs) { - final FileHistoryRefresher refresher = new FileHistoryRefresher(vcsHistoryProvider, annotationProvider, path, repositoryPath, vcs); - refresher.run(false); + final FileHistoryRefresherI refresherI = new FileHistoryRefresher(vcsHistoryProvider, annotationProvider, path, repositoryPath, vcs); + refresherI.run(false); } public void showRollbackChangesDialog(List changes) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/merge/MultipleFileMergeDialog.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/merge/MultipleFileMergeDialog.java index 66077ceb42bd..52fd4fce9305 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/merge/MultipleFileMergeDialog.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/merge/MultipleFileMergeDialog.java @@ -100,7 +100,7 @@ public class MultipleFileMergeDialog extends DialogWrapper { } @Override - public String getMaxStringValue() { + public String getMaxStringValue(JTable table) { return VcsBundle.message("multiple.file.merge.type.binary"); } diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/ui/experts/importToCvs/CustomizeKeywordSubstitutionDialog.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/ui/experts/importToCvs/CustomizeKeywordSubstitutionDialog.java index 2da5db5bb0db..503bb42774b1 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/ui/experts/importToCvs/CustomizeKeywordSubstitutionDialog.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/ui/experts/importToCvs/CustomizeKeywordSubstitutionDialog.java @@ -90,7 +90,7 @@ public class CustomizeKeywordSubstitutionDialog extends DialogWrapper { } @Override - public String getMaxStringValue() { + public String getMaxStringValue(JTable table) { return KeywordSubstitutionWrapper.KEYWORD_EXPANSION_LOCKER.toString(); } }; @@ -118,7 +118,7 @@ public class CustomizeKeywordSubstitutionDialog extends DialogWrapper { } @Override - public String getMaxStringValue() { + public String getMaxStringValue(JTable table) { return getName(); } }; diff --git a/plugins/git4idea/src/git4idea/merge/GitMergeProvider.java b/plugins/git4idea/src/git4idea/merge/GitMergeProvider.java index 44a430482516..27602c194bba 100644 --- a/plugins/git4idea/src/git4idea/merge/GitMergeProvider.java +++ b/plugins/git4idea/src/git4idea/merge/GitMergeProvider.java @@ -40,6 +40,7 @@ import git4idea.i18n.GitBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.io.IOException; import java.util.HashMap; import java.util.List; @@ -420,7 +421,7 @@ public class GitMergeProvider implements MergeProvider2 { } @Override - public String getMaxStringValue() { + public String getMaxStringValue(JTable table) { return GitBundle.message("merge.tool.column.status.modified"); } diff --git a/plugins/git4idea/src/git4idea/ui/GitCommitListPanel.java b/plugins/git4idea/src/git4idea/ui/GitCommitListPanel.java index d0941f3f2b83..42700e67dd78 100644 --- a/plugins/git4idea/src/git4idea/ui/GitCommitListPanel.java +++ b/plugins/git4idea/src/git4idea/ui/GitCommitListPanel.java @@ -197,7 +197,7 @@ public class GitCommitListPanel extends JPanel implements TypeSafeDataProvider { } @Override - public String getMaxStringValue() { + public String getMaxStringValue(JTable table) { return myMaxString; } diff --git a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgTest.java b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgTest.java index b99b33e9f6bb..e9e903ddc874 100644 --- a/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgTest.java +++ b/plugins/hg4idea/testSrc/org/zmlx/hg4idea/test/HgTest.java @@ -21,6 +21,7 @@ import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.AbstractVcsTestCase; +import com.intellij.testFramework.TestClientRunner; import com.intellij.ui.GuiUtils; import com.intellij.util.ui.UIUtil; import com.intellij.vcsUtil.VcsUtil; @@ -185,7 +186,7 @@ public abstract class HgTest extends AbstractVcsTestCase { * @param commandLine command and parameters (e.g. 'status, -m'). */ protected ProcessOutput runHg(@Nullable File workingDir, String... commandLine) throws IOException { - return runClient(HG_EXECUTABLE, null, workingDir, commandLine); + return createClientRunner().runClient(HG_EXECUTABLE, null, workingDir, commandLine); } protected File fillFile(File aParentDir, String[] filePath, String fileContents) throws FileNotFoundException { diff --git a/plugins/svn4idea/src/META-INF/plugin.xml b/plugins/svn4idea/src/META-INF/plugin.xml index a902b4339eba..b3ddb8111795 100644 --- a/plugins/svn4idea/src/META-INF/plugin.xml +++ b/plugins/svn4idea/src/META-INF/plugin.xml @@ -112,6 +112,7 @@ + diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/ConflictedSvnChange.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/ConflictedSvnChange.java index 8ab52bd480a0..1f953c990b2d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/ConflictedSvnChange.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/ConflictedSvnChange.java @@ -19,11 +19,16 @@ import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ContentRevision; +import org.tmatesoft.svn.core.internal.wc.SVNTreeConflictUtil; +import org.tmatesoft.svn.core.wc.SVNTreeConflictDescription; import javax.swing.*; public class ConflictedSvnChange extends Change { private final ConflictState myConflictState; + // also used if not move/rename + private SVNTreeConflictDescription myBeforeDescription; + private SVNTreeConflictDescription myAfterDescription; // +- private final FilePath myTreeConflictMarkHolder; @@ -35,7 +40,8 @@ public class ConflictedSvnChange extends Change { } public ConflictedSvnChange(ContentRevision beforeRevision, ContentRevision afterRevision, FileStatus fileStatus, - final ConflictState conflictState, final FilePath treeConflictMarkHolder) { + final ConflictState conflictState, + final FilePath treeConflictMarkHolder) { super(beforeRevision, afterRevision, fileStatus); myConflictState = conflictState; myTreeConflictMarkHolder = treeConflictMarkHolder; @@ -45,6 +51,27 @@ public class ConflictedSvnChange extends Change { return myConflictState; } + @Override + public boolean isTreeConflict() { + return myConflictState.isTree(); + } + + public SVNTreeConflictDescription getBeforeDescription() { + return myBeforeDescription; + } + + public void setBeforeDescription(SVNTreeConflictDescription beforeDescription) { + myBeforeDescription = beforeDescription; + } + + public SVNTreeConflictDescription getAfterDescription() { + return myAfterDescription; + } + + public void setAfterDescription(SVNTreeConflictDescription afterDescription) { + myAfterDescription = afterDescription; + } + @Override public Icon getAdditionalIcon() { return myConflictState.getIcon(); @@ -54,7 +81,22 @@ public class ConflictedSvnChange extends Change { public String getDescription() { final String description = myConflictState.getDescription(); if (description != null) { - return SvnBundle.message("svn.changeview.item.in.conflict.text", description); + final StringBuilder sb = new StringBuilder(SvnBundle.message("svn.changeview.item.in.conflict.text", description)); + if (myBeforeDescription != null) { + sb.append('\n'); + if (myAfterDescription != null) { + sb.append("before: "); + } + sb.append(SVNTreeConflictUtil.getHumanReadableConflictDescription(myBeforeDescription)); + } + if (myAfterDescription != null) { + sb.append('\n'); + if (myBeforeDescription != null) { + sb.append("after: "); + } + sb.append(SVNTreeConflictUtil.getHumanReadableConflictDescription(myAfterDescription)); + } + return sb.toString(); } return description; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java index 5d021471c14d..541f8a2786bb 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnChangeProviderContext.java @@ -322,14 +322,26 @@ class SvnChangeProviderContext implements StatusReceiver { Change createMovedChange(final ContentRevision before, final ContentRevision after, final SVNStatus copiedStatus, final SVNStatus deletedStatus) throws SVNException { // todo no convertion needed for the contents status? - return patchWithPropertyChange(new ConflictedSvnChange(before, after, ConflictState.mergeState(getState(copiedStatus), getState(deletedStatus)), - ((copiedStatus != null) && (copiedStatus.getTreeConflict() != null)) ? after.getFile() : before.getFile()), copiedStatus, deletedStatus); + final ConflictedSvnChange conflictedSvnChange = + new ConflictedSvnChange(before, after, ConflictState.mergeState(getState(copiedStatus), getState(deletedStatus)), + ((copiedStatus != null) && (copiedStatus.getTreeConflict() != null)) ? after.getFile() : before.getFile()); + if (deletedStatus != null) { + conflictedSvnChange.setBeforeDescription(deletedStatus.getTreeConflict()); + } + if (copiedStatus != null) { + conflictedSvnChange.setAfterDescription(copiedStatus.getTreeConflict()); + } + return patchWithPropertyChange(conflictedSvnChange, copiedStatus, deletedStatus); } private Change createChange(final ContentRevision before, final ContentRevision after, final FileStatus fStatus, final SVNStatus svnStatus) throws SVNException { - return patchWithPropertyChange(new ConflictedSvnChange(before, after, correctContentsStatus(fStatus, svnStatus), - getState(svnStatus), after == null ? before.getFile() : after.getFile()), svnStatus, null); + final ConflictedSvnChange conflictedSvnChange = new ConflictedSvnChange(before, after, correctContentsStatus(fStatus, svnStatus), + getState(svnStatus), after == null ? before.getFile() : after.getFile()); + if (svnStatus != null) { + conflictedSvnChange.setBeforeDescription(svnStatus.getTreeConflict()); + } + return patchWithPropertyChange(conflictedSvnChange, svnStatus, null); } private FileStatus correctContentsStatus(final FileStatus fs, final SVNStatus svnStatus) throws SVNException { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java index 3484c35dce86..0e274c011fbc 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java @@ -16,7 +16,6 @@ package org.jetbrains.idea.svn.history; import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.IconLoader; @@ -25,7 +24,9 @@ import com.intellij.openapi.util.Ref; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.annotate.ShowAllAffectedGenericAction; -import com.intellij.openapi.vcs.changes.ChangesUtil; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.issueLinks.TableLinkMouseListener; import com.intellij.openapi.vcs.history.*; import com.intellij.openapi.vfs.VirtualFile; @@ -43,6 +44,8 @@ import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.SVNErrorManager; +import org.tmatesoft.svn.core.io.SVNRepository; +import org.tmatesoft.svn.core.io.SVNRepositoryFactory; import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.util.SVNLogType; @@ -58,7 +61,7 @@ import java.util.List; import java.util.Map; public class SvnHistoryProvider - implements VcsHistoryProvider, VcsCacheableHistorySessionFactory { + implements VcsHistoryProvider, VcsCacheableHistorySessionFactory { private final SvnVcs myVcs; public SvnHistoryProvider(SvnVcs vcs) { @@ -73,8 +76,8 @@ public class SvnHistoryProvider final ColumnInfo[] columns; final Consumer listener; final JComponent addComp; - if (((MyHistorySession)session).isSupports15()) { - final MergeSourceColumnInfo mergeSourceColumn = new MergeSourceColumnInfo((MyHistorySession)session); + if (((SvnHistorySession)session).isSupports15()) { + final MergeSourceColumnInfo mergeSourceColumn = new MergeSourceColumnInfo((SvnHistorySession)session); columns = new ColumnInfo[]{new CopyFromColumnInfo(), mergeSourceColumn}; final JPanel panel = new JPanel(new BorderLayout()); @@ -119,83 +122,21 @@ public class SvnHistoryProvider } @Override - public FilePath getUsedFilePath(MyHistorySession session) { + public FilePath getUsedFilePath(SvnHistorySession session) { return session.getCommittedPath(); } @Override - public Boolean getAddinionallyCachedData(MyHistorySession session) { + public Boolean getAddinionallyCachedData(SvnHistorySession session) { return session.isSupports15(); } @Override - public MyHistorySession createFromCachedData(Boolean aBoolean, + public SvnHistorySession createFromCachedData(Boolean aBoolean, @NotNull List revisions, @NotNull FilePath filePath, VcsRevisionNumber currentRevision) { - return new MyHistorySession(revisions, filePath, aBoolean, currentRevision, false); - } - - class MyHistorySession extends VcsAbstractHistorySession { - private final FilePath myCommittedPath; - private final boolean mySupports15; - - private MyHistorySession(final List revisions, final FilePath committedPath, final boolean supports15, - @Nullable final VcsRevisionNumber currentRevision, boolean skipRefreshOnStart) { - super(revisions, currentRevision); - myCommittedPath = committedPath; - mySupports15 = supports15; - if (!skipRefreshOnStart) { - shouldBeRefreshed(); - } - } - - public HistoryAsTreeProvider getHistoryAsTreeProvider() { - return null; - } - - @Nullable - public VcsRevisionNumber calcCurrentRevisionNumber() { - if (myCommittedPath == null) { - return null; - } - if (myCommittedPath.isNonLocal()) { - // technically, it does not make sense, since there's no "current" revision for non-local history (if look how it's used) - // but ok, lets keep it for now - return new SvnRevisionNumber(SVNRevision.HEAD); - } - try { - SVNWCClient wcClient = myVcs.createWCClient(); - SVNInfo info = wcClient.doInfo(new File(myCommittedPath.getPath()), SVNRevision.UNDEFINED); - if (info != null) { - return new SvnRevisionNumber(info.getCommittedRevision()); - } - else { - return null; - } - } - catch (SVNException e) { - return null; - } - } - - public FilePath getCommittedPath() { - return myCommittedPath; - } - - @Override - public boolean isContentAvailable(final VcsFileRevision revision) { - return !myCommittedPath.isDirectory(); - } - - public boolean isSupports15() { - return mySupports15; - } - - @Override - public VcsHistorySession copy() { - return new MyHistorySession(getRevisionList(), myCommittedPath, mySupports15, getCurrentRevisionNumber(), true); - } + return new SvnHistorySession(myVcs, revisions, filePath, aBoolean, currentRevision, false); } @Nullable @@ -208,14 +149,32 @@ public class SvnHistoryProvider } public void reportAppendableHistory(FilePath path, final VcsAppendableHistorySessionPartner partner) throws VcsException { - final FilePath committedPath = ChangesUtil.getCommittedPath(myVcs.getProject(), path); + reportAppendableHistory(path, partner, null, null, 0, null, false); + } + + public void reportAppendableHistory(FilePath path, final VcsAppendableHistorySessionPartner partner, + @Nullable final SVNRevision from, @Nullable final SVNRevision to, final int limit, + SVNRevision peg, final boolean forceBackwards) throws VcsException { + FilePath committedPath = path; + Change change = ChangeListManager.getInstance(myVcs.getProject()).getChange(path); + if (change != null) { + final ContentRevision beforeRevision = change.getBeforeRevision(); + final ContentRevision afterRevision = change.getAfterRevision(); + if (beforeRevision != null && afterRevision != null && !beforeRevision.getFile().equals(afterRevision.getFile()) && + afterRevision.getFile().equals(path)) { + committedPath = beforeRevision.getFile(); + } + if (peg == null && change.getBeforeRevision() != null) { + peg = ((SvnRevisionNumber) change.getBeforeRevision().getRevisionNumber()).getRevision(); + } + } final LogLoader logLoader; if (path.isNonLocal()) { - logLoader = new RepositoryLoader(myVcs, path); + logLoader = new RepositoryLoader(myVcs, path, from, to, limit, peg, forceBackwards); } else { - logLoader = new LocalLoader(myVcs, path); + logLoader = new LocalLoader(myVcs, path, from, to, limit, peg); } try { @@ -229,9 +188,8 @@ public class SvnHistoryProvider } logLoader.initSupports15(); - final MyHistorySession historySession = - new MyHistorySession(Collections.emptyList(), committedPath, Boolean.TRUE.equals(logLoader.mySupport15), null, - false); + final SvnHistorySession historySession = + new SvnHistorySession(myVcs, Collections.emptyList(), committedPath, Boolean.TRUE.equals(logLoader.mySupport15), null, false); final Ref sessionReported = new Ref(); final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); @@ -253,56 +211,26 @@ public class SvnHistoryProvider logLoader.check(); } - @Nullable - private void getRevisionsList(final FilePath file, final Ref supports15Ref, - final Consumer consumer) throws VcsException { - final VcsException[] exception = new VcsException[1]; - - Runnable command = new Runnable() { - public void run() { - final ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator(); - if (indicator != null) { - indicator.setText(SvnBundle.message("progress.text2.collecting.history", file.getName())); - } - try { - if (!file.isNonLocal()) { - collectLogEntries(indicator, file, exception, consumer, supports15Ref); - } - else { - collectLogEntriesForRepository(indicator, file, consumer, supports15Ref); - } - } - catch (SVNCancelException ex) { - throw new ProcessCanceledException(ex); - } - catch (SVNException e) { - exception[0] = new VcsException(e); - } - catch (VcsException e) { - exception[0] = e; - } - } - }; - - command.run(); - - if (exception[0] != null) { - throw exception[0]; - } - } - private static abstract class LogLoader { protected String myUrl; protected boolean mySupport15; protected final SvnVcs myVcs; protected final FilePath myFile; + protected final SVNRevision myFrom; + protected final SVNRevision myTo; + protected final int myLimit; + protected final SVNRevision myPeg; protected Consumer myConsumer; protected final ProgressIndicator myPI; protected VcsException myException; - protected LogLoader(SvnVcs vcs, FilePath file) { + protected LogLoader(SvnVcs vcs, FilePath file, SVNRevision from, SVNRevision to, int limit, SVNRevision peg) { myVcs = vcs; myFile = file; + myFrom = from; + myTo = to; + myLimit = limit; + myPeg = peg; myPI = ProgressManager.getInstance().getProgressIndicator(); } @@ -327,8 +255,8 @@ public class SvnHistoryProvider private static class LocalLoader extends LogLoader { private SVNInfo myInfo; - private LocalLoader(SvnVcs vcs, FilePath file) { - super(vcs, file); + private LocalLoader(SvnVcs vcs, FilePath file, SVNRevision from, SVNRevision to, int limit, SVNRevision peg) { + super(vcs, file, from, to, limit, peg); } @Override @@ -370,8 +298,9 @@ public class SvnHistoryProvider SVNLogClient client = myVcs.createLogClient(); try { client - .doLog(new File[]{new File(myFile.getIOFile().getAbsolutePath())}, SVNRevision.HEAD, SVNRevision.create(1), SVNRevision.UNDEFINED, - false, true, mySupport15, 0, null, + .doLog(new File[]{new File(myFile.getIOFile().getAbsolutePath())}, + myFrom == null ? SVNRevision.HEAD : myFrom, myTo == null ? SVNRevision.create(1) : myTo, myPeg, + false, true, mySupport15, myLimit, null, new MyLogEntryHandler(myVcs, myUrl, pegRevision, relativeUrl, myConsumer, repoRootURL, myFile.getCharset())); } catch (SVNCancelException e) { @@ -386,47 +315,18 @@ public class SvnHistoryProvider } } - private void collectLogEntries(final ProgressIndicator indicator, FilePath file, VcsException[] exception, - final Consumer result, final Ref supports15Ref) throws SVNException, - VcsException { - SVNWCClient wcClient = myVcs.createWCClient(); - SVNInfo info = wcClient.doInfo(new File(file.getIOFile().getAbsolutePath()), SVNRevision.UNDEFINED); - wcClient.setEventHandler(new ISVNEventHandler() { - public void handleEvent(SVNEvent event, double progress) throws SVNException { - } - - public void checkCancelled() throws SVNCancelException { - indicator.checkCanceled(); - } - }); - if (info == null || info.getRepositoryRootURL() == null) { - exception[0] = new VcsException("File ''{0}'' is not under version control" + file.getIOFile()); - return; - } - final String url = info.getURL() == null ? null : info.getURL().toString(); - String relativeUrl = url; - final SVNURL repoRootURL = info.getRepositoryRootURL(); - - final String root = repoRootURL.toString(); - if (url != null && url.startsWith(root)) { - relativeUrl = url.substring(root.length()); - } - if (indicator != null) { - indicator.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", url)); - } - final SVNRevision pegRevision = info.getRevision(); - SVNLogClient client = myVcs.createLogClient(); - - final boolean supports15 = SvnUtil.checkRepositoryVersion15(myVcs, url); - supports15Ref.set(supports15); - client.doLog(new File[]{new File(file.getIOFile().getAbsolutePath())}, SVNRevision.HEAD, SVNRevision.create(1), SVNRevision.UNDEFINED, - false, true, supports15, 0, null, - new MyLogEntryHandler(myVcs, url, pegRevision, relativeUrl, result, repoRootURL, file.getCharset())); - } - private static class RepositoryLoader extends LogLoader { - private RepositoryLoader(SvnVcs vcs, FilePath file) { - super(vcs, file); + private final boolean myForceBackwards; + + private RepositoryLoader(SvnVcs vcs, + FilePath file, + SVNRevision from, + SVNRevision to, + int limit, + SVNRevision peg, + boolean forceBackwards) { + super(vcs, file, from, to, limit, peg); + myForceBackwards = forceBackwards; } @Override @@ -439,20 +339,27 @@ public class SvnHistoryProvider if (myPI != null) { myPI.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", myUrl)); } + if (myForceBackwards) { + loadBackwards(); + return; + } + SVNWCClient wcClient = myVcs.createWCClient(); try { final SVNURL svnurl = SVNURL.parseURIEncoded(myUrl); SVNInfo info = null; - info = wcClient.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.HEAD); + SVNRevision operationalFrom = myFrom == null ? SVNRevision.HEAD : myFrom; + info = wcClient.doInfo(svnurl, myPeg, operationalFrom); final String root = info.getRepositoryRootURL().toString(); String relativeUrl = myUrl; if (myUrl.startsWith(root)) { relativeUrl = myUrl.substring(root.length()); } SVNLogClient client = myVcs.createLogClient(); - client - .doLog(svnurl, new String[]{}, SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNRevision.create(1), false, true, mySupport15, 0, null, - new RepositoryLogEntryHandler(myVcs, myUrl, SVNRevision.UNDEFINED, relativeUrl, myConsumer, info.getRepositoryRootURL())); + client.doLog(svnurl, new String[]{}, myPeg, + operationalFrom, myTo == null ? SVNRevision.create(1) : myTo, false, true, mySupport15, myLimit, null, + new RepositoryLogEntryHandler(myVcs, myUrl, SVNRevision.UNDEFINED, relativeUrl, myConsumer, + info.getRepositoryRootURL())); } catch (SVNCancelException e) { // @@ -464,56 +371,57 @@ public class SvnHistoryProvider myException = e; } } - } - private void collectLogEntriesForRepository(final ProgressIndicator indicator, FilePath file, final Consumer result, - final Ref supports15Ref) throws SVNException, VcsException { - final String url = file.getPath().replace('\\', '/'); - if (indicator != null) { - indicator.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", url)); + private void loadBackwards() { + try { + final SVNURL svnurl = SVNURL.parseURIEncoded(myUrl); + SVNRevision operationalFrom = myFrom == null ? SVNRevision.HEAD : myFrom; + final SVNURL rootURL = getRepositoryRoot(svnurl, operationalFrom); + final String root = rootURL.toString(); + String relativeUrl = myUrl; + if (myUrl.startsWith(root)) { + relativeUrl = myUrl.substring(root.length()); + } + + SVNLogClient client = myVcs.createLogClient(); + + final RepositoryLogEntryHandler repositoryLogEntryHandler = + new RepositoryLogEntryHandler(myVcs, myUrl, SVNRevision.UNDEFINED, relativeUrl, myConsumer, rootURL); + repositoryLogEntryHandler.setThrowCancelOnMeetPathCreation(true); + + SVNRevision current = operationalFrom; + client.doLog(rootURL, new String[]{}, current, myTo, current, false, true, mySupport15, myLimit, null, repositoryLogEntryHandler); + } + catch (SVNCancelException e) { + // + } + catch (SVNException e) { + myException = new VcsException(e); + } + catch (VcsException e) { + myException = e; + } } - SVNWCClient wcClient = myVcs.createWCClient(); - final SVNURL svnurl = SVNURL.parseURIEncoded(url); - SVNInfo info = wcClient.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.HEAD); - final String root = info.getRepositoryRootURL().toString(); - String relativeUrl = url; - if (url.startsWith(root)) { - relativeUrl = url.substring(root.length()); + + private SVNURL getRepositoryRoot(SVNURL svnurl, SVNRevision operationalFrom) throws SVNException { + final SVNWCClient wcClient = myVcs.createWCClient(); + try { + final SVNInfo info; + info = wcClient.doInfo(svnurl, myPeg, operationalFrom); + return info.getRepositoryRootURL(); + } + catch (SVNException e) { + final SVNInfo info; + info = wcClient.doInfo(svnurl, SVNRevision.UNDEFINED, SVNRevision.UNDEFINED); + return info.getRepositoryRootURL(); + } } - SVNLogClient client = myVcs.createLogClient(); - final boolean supports15 = SvnUtil.checkRepositoryVersion15(myVcs, root); - supports15Ref.set(supports15); - // todo log in history provider - client.doLog(svnurl, new String[]{}, SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNRevision.create(1), false, true, supports15, 0, null, - new RepositoryLogEntryHandler(myVcs, url, SVNRevision.UNDEFINED, relativeUrl, result, info.getRepositoryRootURL())); } public String getHelpId() { return null; } - @Nullable - private VcsRevisionNumber getCurrentRevision(FilePath file) { - if (file.isNonLocal()) { - // technically, it does not make sense, since there's no "current" revision for non-local history (if look how it's used) - // but ok, lets keep it for now - return new SvnRevisionNumber(SVNRevision.HEAD); - } - try { - SVNWCClient wcClient = myVcs.createWCClient(); - SVNInfo info = wcClient.doInfo(new File(file.getPath()).getAbsoluteFile(), SVNRevision.UNDEFINED); - if (info != null) { - return new SvnRevisionNumber(info.getRevision()); - } - else { - return null; - } - } - catch (SVNException e) { - return null; - } - } - public AnAction[] getAdditionalActions(final Runnable refresher) { return new AnAction[]{new ShowAllAffectedGenericAction(), new MergeSourceDetailsAction()}; } @@ -533,6 +441,11 @@ public class SvnHistoryProvider protected final String myUrl; private final SvnMergeSourceTracker myTracker; protected SVNURL myRepositoryRoot; + private boolean myThrowCancelOnMeetPathCreation; + + public void setThrowCancelOnMeetPathCreation(boolean throwCancelOnMeetPathCreation) { + myThrowCancelOnMeetPathCreation = throwCancelOnMeetPathCreation; + } public MyLogEntryHandler(SvnVcs vcs, final String url, final SVNRevision pegRevision, @@ -575,6 +488,7 @@ public class SvnHistoryProvider path = SVNPathUtil.removeTail(path); } } + if (entryPath == null) return; // skip this revision: no our path in it final int mergeLevel = svnLogEntryIntegerPair.getSecond(); final SvnFileRevision revision = createRevision(logEntry, copyPath, entryPath); @@ -590,6 +504,9 @@ public class SvnHistoryProvider myResult.consume(revision); myPrevious = revision; } + if (myThrowCancelOnMeetPathCreation && myUrl.equals(revision.getURL()) && entryPath.getType() == 'A') { + throw new SVNCancelException(); + } } }); @@ -662,7 +579,7 @@ public class SvnHistoryProvider private class MergeSourceColumnInfo extends ColumnInfo { private final MergeSourceRenderer myRenderer; - private MergeSourceColumnInfo(final MyHistorySession session) { + private MergeSourceColumnInfo(final SvnHistorySession session) { super("Merge Sources"); myRenderer = new MergeSourceRenderer(session); } @@ -743,7 +660,7 @@ public class SvnHistoryProvider private MergeSourceDetailsLinkListener myListener; private final VirtualFile myFile; - private MergeSourceRenderer(final MyHistorySession session) { + private MergeSourceRenderer(final SvnHistorySession session) { myFile = session.getCommittedPath().getVirtualFile(); } @@ -850,7 +767,7 @@ public class SvnHistoryProvider } @Override - public String getMaxStringValue() { + public String getMaxStringValue(JTable table) { return SvnBundle.message("copy.column.title"); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java new file mode 100644 index 000000000000..d8ba5f1f849a --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistorySession.java @@ -0,0 +1,104 @@ +/* + * 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 org.jetbrains.idea.svn.history; + +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.history.*; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnRevisionNumber; +import org.jetbrains.idea.svn.SvnVcs; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNWCClient; + +import java.io.File; +import java.util.List; + +/** +* Created with IntelliJ IDEA. +* User: Irina.Chernushina +* Date: 4/27/12 +* Time: 12:24 PM +* To change this template use File | Settings | File Templates. +*/ +public class SvnHistorySession extends VcsAbstractHistorySession { + private final SvnVcs myVcs; + private final FilePath myCommittedPath; + private final boolean mySupports15; + + public SvnHistorySession(SvnVcs vcs, final List revisions, final FilePath committedPath, final boolean supports15, + @Nullable final VcsRevisionNumber currentRevision, boolean skipRefreshOnStart) { + super(revisions, currentRevision); + myVcs = vcs; + myCommittedPath = committedPath; + mySupports15 = supports15; + if (!skipRefreshOnStart) { + shouldBeRefreshed(); + } + } + + public HistoryAsTreeProvider getHistoryAsTreeProvider() { + return null; + } + + @Nullable + public VcsRevisionNumber calcCurrentRevisionNumber() { + if (myCommittedPath == null) { + return null; + } + if (myCommittedPath.isNonLocal()) { + // technically, it does not make sense, since there's no "current" revision for non-local history (if look how it's used) + // but ok, lets keep it for now + return new SvnRevisionNumber(SVNRevision.HEAD); + } + return getCurrentCommittedRevision(myVcs, new File(myCommittedPath.getPath())); + } + + public static VcsRevisionNumber getCurrentCommittedRevision(final SvnVcs vcs, final File file) { + try { + SVNWCClient wcClient = vcs.createWCClient(); + SVNInfo info = wcClient.doInfo(file, SVNRevision.UNDEFINED); + if (info != null) { + return new SvnRevisionNumber(info.getCommittedRevision()); + } + else { + return null; + } + } + catch (SVNException e) { + return null; + } + } + + public FilePath getCommittedPath() { + return myCommittedPath; + } + + @Override + public boolean isContentAvailable(final VcsFileRevision revision) { + return !myCommittedPath.isDirectory(); + } + + public boolean isSupports15() { + return mySupports15; + } + + @Override + public VcsHistorySession copy() { + return new SvnHistorySession(myVcs, getRevisionList(), myCommittedPath, mySupports15, getCurrentRevisionNumber(), true); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/SvnTreeConflictResolver.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/SvnTreeConflictResolver.java new file mode 100644 index 000000000000..0cc7b3c7e904 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/SvnTreeConflictResolver.java @@ -0,0 +1,149 @@ +/* + * 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 org.jetbrains.idea.svn.treeConflict; + +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; +import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnRevisionNumber; +import org.jetbrains.idea.svn.SvnVcs; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.*; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +/** +* Created with IntelliJ IDEA. +* User: Irina.Chernushina +* Date: 5/2/12 +* Time: 1:03 PM +*/ +public class SvnTreeConflictResolver { + private final SvnVcs myVcs; + private final FilePath myPath; + private VcsRevisionNumber myCommittedRevision; + private final FilePath myRevertPath; + private final VcsDirtyScopeManager myDirtyScopeManager; + + public SvnTreeConflictResolver(SvnVcs vcs, FilePath path, VcsRevisionNumber committedRevision, final @Nullable FilePath revertPath) { + myVcs = vcs; + myPath = path; + myCommittedRevision = committedRevision; + myRevertPath = revertPath; + myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myVcs.getProject()); + } + + public void resolveSelectTheirsFull(SVNTreeConflictDescription d) throws VcsException { + updatetoTheirsFull(); + pathDirty(myPath); + revertAdditional(); + } + + private void pathDirty(final FilePath path) { + if (path.isDirectory()) { + myDirtyScopeManager.dirDirtyRecursively(path); + } + else { + myDirtyScopeManager.fileDirty(path); + } + } + + private void revertAdditional() throws VcsException { + if (myRevertPath == null) return; + final File ioFile = myRevertPath.getIOFile(); + SVNWCClient client = myVcs.createWCClient(); + try { + client.doRevert(new File[]{ioFile}, SVNDepth.INFINITY, null); + } + catch (SVNException e) { + throw new VcsException(e); + } + pathDirty(myRevertPath); + } + + public void resolveSelectMineFull(SVNTreeConflictDescription d) throws VcsException { + SVNWCClient client = myVcs.createWCClient(); + try { + client.doResolve(myPath.getIOFile(), SVNDepth.INFINITY, SVNConflictChoice.MERGED); + } + catch (SVNException e) { + throw new VcsException(e); + } + pathDirty(myPath); + revertAdditional(); + } + + private void updatetoTheirsFull() throws VcsException { + try { + final File ioFile = myPath.getIOFile(); + SVNWCClient client = myVcs.createWCClient(); + SVNStatusClient statusClient = myVcs.createStatusClient(); + SVNStatus status = statusClient.doStatus(ioFile, false); + if (myCommittedRevision == null) { + myCommittedRevision = new SvnRevisionNumber(status.getCommittedRevision()); + } + if (status == null || SVNStatusType.STATUS_UNVERSIONED.equals(status.getNodeStatus())) { + client.doRevert(new File[]{ioFile}, SVNDepth.INFINITY, null); + //updateIoFile(ioFile, SVNRevision.HEAD); + return; +// FileUtil.delete(ioFile); + } else if (SVNStatusType.STATUS_ADDED.equals(status.getNodeStatus())) { + client.doRevert(new File[]{ioFile}, SVNDepth.INFINITY, null); + //updateIoFile(ioFile, SVNRevision.HEAD); + /*client.doRevert(new File[]{ioFile}, SVNDepth.INFINITY, null); + FileUtil.delete(ioFile);*/ + return; + } else { + final Set usedToBeAdded = new HashSet(); + if (myPath.isDirectory()) { + statusClient.doStatus(ioFile, SVNRevision.UNDEFINED, SVNDepth.INFINITY, false, false, false, false, + new ISVNStatusHandler() { + @Override + public void handleStatus(SVNStatus status) throws SVNException { + if (status != null && SVNStatusType.STATUS_ADDED.equals(status.getNodeStatus())) { + usedToBeAdded.add(status.getFile()); + } + } + }, null); + } + client.doRevert(new File[]{ioFile}, SVNDepth.INFINITY, null); + } + + /*if (myPath.isDirectory()) { + if (myCommittedRevision != null) { + updateIoFile(ioFile, ((SvnRevisionNumber) myCommittedRevision).getRevision()); + } + updateIoFile(ioFile, SVNRevision.HEAD); + }*/ + } + catch (SVNException e1) { + throw new VcsException(e1); + } + } + + private void updateIoFile(File ioFile, final SVNRevision revision) throws SVNException { + if (! ioFile.exists()) { + myVcs.createUpdateClient().doUpdate(ioFile.getParentFile(), revision, SVNDepth.INFINITY, true, false); + } else { + myVcs.createUpdateClient().doUpdate(ioFile, revision, SVNDepth.INFINITY, false, false); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictDetailsProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictDetailsProvider.java new file mode 100644 index 000000000000..24167e5f1bb8 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictDetailsProvider.java @@ -0,0 +1,55 @@ +/* + * 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 org.jetbrains.idea.svn.treeConflict; + +import com.intellij.openapi.progress.BackgroundTaskQueue; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.RefreshablePanel; +import com.intellij.openapi.vcs.changes.VcsChangeDetailsProvider; +import org.jetbrains.idea.svn.ConflictedSvnChange; + +import javax.swing.*; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 4/25/12 + * Time: 5:04 PM + */ +public class TreeConflictDetailsProvider implements VcsChangeDetailsProvider { + private final Project myProject; + + public TreeConflictDetailsProvider(Project project) { + myProject = project; + } + + @Override + public String getName() { + return "Subversion Tree Conflict"; + } + + @Override + public boolean canComment(Change change) { + if (change instanceof ConflictedSvnChange && ((ConflictedSvnChange)change).getConflictState().isTree()) return true; + return false; + } + + @Override + public RefreshablePanel comment(Change change, JComponent parent, BackgroundTaskQueue queue) { + return new TreeConflictRefreshablePanel(myProject, "Loading tree conflict details", queue, change); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java new file mode 100644 index 000000000000..62e0916d628e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java @@ -0,0 +1,681 @@ +/* + * 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 org.jetbrains.idea.svn.treeConflict; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.progress.BackgroundTaskQueue; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.history.*; +import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; +import com.intellij.util.BeforeAfter; +import com.intellij.util.ThrowableRunnable; +import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.VcsBackgroundTask; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.ConflictedSvnChange; +import org.jetbrains.idea.svn.SvnRevisionNumber; +import org.jetbrains.idea.svn.SvnUtil; +import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.history.SvnHistoryProvider; +import org.jetbrains.idea.svn.history.SvnHistorySession; +import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNNodeKind; +import org.tmatesoft.svn.core.internal.wc.SVNConflictVersion; +import org.tmatesoft.svn.core.internal.wc.SVNTreeConflictUtil; +import org.tmatesoft.svn.core.wc.*; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 4/25/12 + * Time: 5:33 PM + */ +public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel { + private final ConflictedSvnChange myChange; + private final SvnVcs myVcs; + private VcsRevisionNumber myCommittedRevision; + private FilePath myPath; + private final List myChildDisposables; + + public TreeConflictRefreshablePanel(Project project, String loadingTitle, BackgroundTaskQueue queue, Change change) { + super(project, loadingTitle, queue); + myVcs = SvnVcs.getInstance(project); + assert change instanceof ConflictedSvnChange; + myChange = (ConflictedSvnChange) change; + myPath = ChangesUtil.getFilePath(myChange); + myChildDisposables = new ArrayList(); + } + + @Override + public boolean isStillValid(final Change change) { + return change.isTreeConflict() && change instanceof ConflictedSvnChange && + descriptionsEqual(((ConflictedSvnChange)change).getBeforeDescription(), myChange.getBeforeDescription()); + } + + private boolean descriptionsEqual(SVNTreeConflictDescription d1, SVNTreeConflictDescription d2) { + if (d1.isPropertyConflict() != d2.isPropertyConflict()) return false; + if (d1.isTextConflict() != d2.isTextConflict()) return false; + if (d1.isTreeConflict() != d2.isTreeConflict()) return false; + + if (! d1.getOperation().equals(d2.getOperation())) return false; + if (! d1.getConflictAction().equals(d2.getConflictAction())) return false; + if (! Comparing.equal(d1.getConflictReason(), d2.getConflictReason())) return false; + if (! Comparing.equal(d1.getPath(), d2.getPath())) return false; + if (! Comparing.equal(d1.getNodeKind(), d2.getNodeKind())) return false; + if (! compareConflictVersion(d1.getSourceLeftVersion(), d2.getSourceLeftVersion())) return false; + if (! compareConflictVersion(d1.getSourceRightVersion(), d2.getSourceRightVersion())) return false; + return true; + } + + private boolean compareConflictVersion(SVNConflictVersion v1, SVNConflictVersion v2) { + if (v1 == null && v2 == null) return true; + if (v1 == null && v2 != null || v1 != null && v2 == null) return false; + if (! v1.getKind().equals(v2.getKind())) return false; + if (! v1.getPath().equals(v2.getPath())) return false; + if (v1.getPegRevision() != v2.getPegRevision()) return false; + if (! Comparing.equal(v1.getRepositoryRoot(), v2.getRepositoryRoot())) return false; + return true; + } + + @Override + protected void refreshPresentation() { + } + + @Override + protected Object loadImpl() throws VcsException { + return new BeforeAfter>(processDescription(myChange.getBeforeDescription()), + processDescription(myChange.getAfterDescription())); + } + + private BeforeAfter processDescription(SVNTreeConflictDescription description) throws VcsException { + if (description == null) return null; + if (myChange.getBeforeRevision() != null) { + myCommittedRevision = SvnHistorySession.getCurrentCommittedRevision(myVcs, myPath.getIOFile()); + } + final SVNRevision pegFromLeft = description.getSourceLeftVersion() == null ? + null : SVNRevision.create(description.getSourceLeftVersion().getPegRevision()); + ConflictSidePresentation rightSide = createSide(description.getSourceRightVersion(), pegFromLeft, false); + final SidesProcessorMarker marker; + ConflictSidePresentation leftSide; + if (description.getSourceLeftVersion() != null && description.getSourceRightVersion() != null && + ! Comparing.equal(description.getSourceLeftVersion().getPath(), description.getSourceRightVersion().getPath())) { + leftSide = createSide(description.getSourceLeftVersion(), pegFromLeft, true); + marker = new TwoSidesProcessor((AbstractConflictSide) leftSide, (AbstractConflictSide) rightSide, UniversalComparator.getInstance()); + } else { + leftSide = EmptyConflictSide.getInstance(); + if (rightSide instanceof AbstractConflictSide) { + marker = new OneSideProcessor( + description.getSourceLeftVersion() == null ? SVNRevision.create(1) : SVNRevision.create(description.getSourceLeftVersion().getPegRevision()), (AbstractConflictSide) rightSide); + } else { + marker = SidesProcessorMarker.EMPTY; + } + } + marker.run(); + myChildDisposables.add(leftSide); + myChildDisposables.add(rightSide); + return new BeforeAfter(leftSide, rightSide); + } + + private static class UniversalComparator implements Comparator { + private final static UniversalComparator ourComparator = new UniversalComparator(); + + public static UniversalComparator getInstance() { + return ourComparator; + } + + @Override + public int compare(Object o1, Object o2) { + long number1 = get(o1).getNumber(); + long number2 = get(o2).getNumber(); + return number1 < number2 ? -1 : (number1 == number2 ? 0 : 1); + } + + private SVNRevision get(final Object o) { + if (o instanceof VcsFileRevision) { + return ((SvnRevisionNumber) ((VcsFileRevision) o).getRevisionNumber()).getRevision(); + } + if (o instanceof CommittedChangeList) { + return SVNRevision.create(((CommittedChangeList) o).getNumber()); + } + assert true; + return null; + } + } + + private ConflictSidePresentation createSide(SVNConflictVersion version, final SVNRevision pegFromLeft, final boolean isLeft) throws VcsException { + if (version == null) return EmptyConflictSide.getInstance(); + SvnRevisionNumber number = null; + if (myChange.getBeforeRevision() != null) { + number = (SvnRevisionNumber) myCommittedRevision; + if (isLeft && number.getRevision().isValid() && number.getRevision().getNumber() == version.getPegRevision()) { + return EmptyConflictSide.getInstance(); + } + } + // todo temporally + /*if (SVNNodeKind.DIR.equals(version.getKind())) { + return new HistoryAsBrowseChangesConflictSide(myVcs.getProject(), version); + } else { + return new HistoryConflictSide(myVcs, version); + }*/ + return new HistoryConflictSide(myVcs, version, number == null ? pegFromLeft : number.getRevision()); + } + + @Override + protected JPanel dataToPresentation(Object o) { + final BeforeAfter> ba = (BeforeAfter>) o; + final JPanel wrapper = new JPanel(new BorderLayout()); + final JPanel main = new JPanel(new GridBagLayout()); + + final GridBagConstraints gb = new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, + new Insets(1, 1, 1, 1), 0, 0); + final String pathComment = myCommittedRevision == null ? "" : + (new StringBuilder(" (current: ") + .append(myChange.getBeforeRevision().getRevisionNumber().asString()).append(", committed: ") + .append(myCommittedRevision.asString()).append(")").toString()); + final JLabel name = new JLabel(myPath.getName() + pathComment); + name.setFont(name.getFont().deriveFont(Font.BOLD)); + gb.insets.top = 5; + main.add(name, gb); + ++ gb.gridy; + gb.insets.top = 10; + appendDescription(myChange.getBeforeDescription(), main, gb, ba.getBefore(), myPath.isDirectory()); + appendDescription(myChange.getAfterDescription(), main, gb, ba.getAfter(), myPath.isDirectory()); + wrapper.add(main, BorderLayout.NORTH); + return wrapper; + } + + private void appendDescription(SVNTreeConflictDescription description, + JPanel main, + GridBagConstraints gb, + BeforeAfter ba, boolean directory) { + if (description == null) return; + JLabel descriptionLbl = new JLabel(SVNTreeConflictUtil.getHumanReadableConflictDescription(description)); + descriptionLbl.setForeground(Color.red); + main.add(descriptionLbl, gb); + ++ gb.gridy; + //buttons + gb.insets.top = 0; + addResolveButtons(description, main, gb); + + addSide(main, gb, ba.getBefore(), description.getSourceLeftVersion(), "Left", directory); + addSide(main, gb, ba.getAfter(), description.getSourceRightVersion(), "Right", directory); + } + + private void addResolveButtons(SVNTreeConflictDescription description, JPanel main, GridBagConstraints gb) { + JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT)); + final JButton both = new JButton("Both"); + final JButton merge = new JButton("Merge"); + final JButton left = new JButton("Mine"); + final JButton right = new JButton("Theirs"); + enableAndSetListener(createBoth(description), both); + enableAndSetListener(createMerge(description), merge); + enableAndSetListener(createLeft(description), left); + enableAndSetListener(createRight(description), right); + /*wrapper.add(both); + wrapper.add(merge);*/ + wrapper.add(left); + wrapper.add(right); + main.add(wrapper, gb); + ++ gb.gridy; + } + + private ActionListener createRight(final SVNTreeConflictDescription description) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ProgressManager.getInstance().run( + new VcsBackgroundTask(myVcs.getProject(), "Accept theirs for: " + myPath, + BackgroundFromStartOption.getInstance(), Collections.singletonList(description), + true) { + @Override + protected void process(SVNTreeConflictDescription d) throws VcsException { + new SvnTreeConflictResolver(myVcs, myPath, myCommittedRevision, null).resolveSelectTheirsFull(d); + } + }); + } + }; + } + + private void acceptOne(final SVNTreeConflictDescription description, final SVNConflictChoice choice, final String title) { + ProgressManager.getInstance().run(new VcsBackgroundTask(myVcs.getProject(), title + myPath, + BackgroundFromStartOption.getInstance(), Collections.singletonList(description), true) { + @Override + protected void process(SVNTreeConflictDescription d) throws VcsException { + try { + myVcs.createWCClient().doResolve(d.getPath(), SVNDepth.INFINITY, choice); + } + catch (SVNException e1) { + throw new VcsException(e1); + } + VcsDirtyScopeManager dirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); + if (myPath.isDirectory()) { + dirtyScopeManager.dirDirtyRecursively(myPath); + } else { + dirtyScopeManager.fileDirty(myPath); + } + } + }); + } + + private ActionListener createLeft(final SVNTreeConflictDescription description) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ProgressManager.getInstance().run( + new VcsBackgroundTask(myVcs.getProject(), "Accept theirs for: " + myPath, + BackgroundFromStartOption.getInstance(), Collections.singletonList(description), + true) { + @Override + protected void process(SVNTreeConflictDescription d) throws VcsException { + new SvnTreeConflictResolver(myVcs, myPath, myCommittedRevision, null).resolveSelectMineFull(d); + } + }); + } + }; + } + + private ActionListener createMerge(SVNTreeConflictDescription description) { + return null; //To change body of created methods use File | Settings | File Templates. + } + + private ActionListener createBoth(SVNTreeConflictDescription description) { + return null; //To change body of created methods use File | Settings | File Templates. + } + + private void enableAndSetListener(final ActionListener al, final JButton b) { + if (al == null) { + b.setEnabled(false); + } + else { + b.addActionListener(al); + } + } + + private void addSide(JPanel main, + GridBagConstraints gb, + ConflictSidePresentation before, + SVNConflictVersion leftVersion, final String name, boolean directory) { + final String leftPresentation = leftVersion == null ? name + ": (" + (directory ? "directory" : "file") + + (myChange.getBeforeRevision() == null ? ") added" : ") unversioned") : + (name + ": " + FileUtil.toSystemIndependentName(SVNTreeConflictUtil.getHumanReadableConflictVersion(leftVersion))); + gb.insets.top = 10; + main.add(new JLabel(leftPresentation), gb); + ++ gb.gridy; + gb.insets.top = 0; + + if (before != null) { + JPanel panel = before.createPanel(); + if (panel != null) { + //gb.fill = GridBagConstraints.HORIZONTAL; + main.add(panel, gb); + //gb.fill = GridBagConstraints.NONE; + ++ gb.gridy; + } + } + } + + @Override + protected void disposeImpl() { + for (Disposable disposable : myChildDisposables) { + Disposer.dispose(disposable); + } + } + + @Override + public void away() { + } + + private interface ConflictSidePresentation extends Disposable { + JPanel createPanel(); + } + + private static class EmptyConflictSide implements ConflictSidePresentation { + private final static EmptyConflictSide ourInstance = new EmptyConflictSide(); + + public static EmptyConflictSide getInstance() { + return ourInstance; + } + + @Override + public JPanel createPanel() { + return null; + } + + @Override + public void dispose() { + } + } + + private static abstract class AbstractConflictSide implements ConflictSidePresentation { + protected final Project myProject; + protected final SVNConflictVersion myVersion; + + private AbstractConflictSide(Project project, SVNConflictVersion version) { + myProject = project; + myVersion = version; + } + + public abstract List step(StopMarker marker) throws VcsException; + public abstract void cutTo(final T t); + } + + private interface StopMarker { + boolean isEof(final List list); + } + + private static class ToRevision implements StopMarker { + private final SVNRevision myRevision; + + private ToRevision(SVNRevision revision) { + myRevision = revision; + } + + public SVNRevision getRevision() { + return myRevision; + } + + @Override + public boolean isEof(List list) { + // should be ok, check would have be like an assertion -> we asked until the revision + return true; + } + } + + private static class Portion implements StopMarker { + private final static int ourStep = 10; + + public static int getOurStep() { + return ourStep; + } + + @Override + public boolean isEof(List list) { + return list.size() < ourStep; + } + } + + private static class NoLimit implements StopMarker { + private final static NoLimit ourInstance = new NoLimit(); + + public static NoLimit getInstance() { + return ourInstance; + } + + @Override + public boolean isEof(List list) { + // we loaded all + return true; + } + } + + private interface SidesProcessorMarker extends ThrowableRunnable { + SidesProcessorMarker EMPTY = new SidesProcessorMarker() { + @Override + public void run() { + } + }; + } + + private static class OneSideProcessor implements SidesProcessorMarker { + private final AbstractConflictSide mySide; + private final SVNRevision myLimitingRevision; + + private OneSideProcessor(SVNRevision limitingRevision, AbstractConflictSide side) { + myLimitingRevision = limitingRevision; + mySide = side; + } + + @Override + public void run() throws VcsException { + mySide.step(new ToRevision(myLimitingRevision)); + } + } + + private static class TwoSidesProcessor implements SidesProcessorMarker { + private final AbstractConflictSide myLeft; + private final AbstractConflictSide myRight; + private final Comparator myComparator; + + private TwoSidesProcessor(@NotNull AbstractConflictSide left, @NotNull AbstractConflictSide right, + final Comparator comparator) { + myLeft = left; + myRight = right; + myComparator = comparator; + } + + @Override + public void run() throws VcsException { + final SteppableSide left = new SteppableSide(myLeft); + final SteppableSide right = new SteppableSide(myRight); + + left.init(); + right.init(); + + while (! left.isEof() || ! right.isEof()) { + while (left.hasNext() && right.hasNext()) { + Left leftItem = left.get(); + Right rightItem = right.get(); + int compare = myComparator.compare(leftItem, rightItem); + if (compare == 0) { + myLeft.cutTo(leftItem); + myRight.cutTo(rightItem); + return; + } else if (compare < 0) { + left.step(); + } else { + right.step(); + } + } + boolean loadLeft = ! left.hasNext(); + left.loadStep(loadLeft, right.isEof()); + right.loadStep(! loadLeft, left.isEof()); + } + } + + private static class SteppableSide { + private List myList; + private int myIdx; + private boolean myEof; + private AbstractConflictSide mySide; + + private SteppableSide(AbstractConflictSide side) { + mySide = side; + myList = Collections.emptyList(); + myEof = false; + myIdx = 0; + } + + public void init() throws VcsException { + loadPiece(false); + } + + public boolean isEof() { + return myEof; + } + + public void loadStep(final boolean forceAdvanceMe, final boolean foreignEof) throws VcsException { + if ((forceAdvanceMe || foreignEof) && ! myEof) { + loadPiece(foreignEof); + } + } + + public void step() { + ++ myIdx; + } + + public T get() { + return myList.get(myIdx); + } + + public boolean hasNext() { + return myIdx < myList.size(); + } + + private void loadPiece(boolean foreignEof) throws VcsException { + StopMarker stopMarker = foreignEof ? NoLimit.getInstance() : new Portion(); + myList = mySide.step(stopMarker); + myEof = stopMarker.isEof(myList); + myList.clear(); + myIdx = 0; + } + } + + private boolean loadPiece(boolean thisEof, boolean foreignEof, final List list) throws VcsException { + //if ((loadThis || foreignEof) && ! thisEof) { + StopMarker stopMarker = foreignEof ? NoLimit.getInstance() : new Portion(); + List step = myLeft.step(stopMarker); + thisEof = stopMarker.isEof(step); + list.clear(); + list.addAll(step); + //} + return thisEof; + } + } + + private static class HistoryConflictSide extends AbstractConflictSide { + private final VcsAppendableHistoryPartnerAdapter mySessionAdapter; + private final SvnHistoryProvider myProvider; + private final FilePath myPath; + private final SvnVcs myVcs; + private final SVNRevision myPeg; + private SVNRevision myRevisionTo; + private FileHistoryPanelImpl myFileHistoryPanel; + + private HistoryConflictSide(SvnVcs vcs, SVNConflictVersion version, final SVNRevision peg) throws VcsException { + super(vcs.getProject(), version); + myVcs = vcs; + myPeg = peg; + try { + myPath = FilePathImpl.createNonLocal( + version.getRepositoryRoot().appendPath(FileUtil.toSystemIndependentName(version.getPath()), true).toString(), SVNNodeKind.DIR.equals(version.getKind())); + } + catch (SVNException e) { + throw new VcsException(e); + } + + mySessionAdapter = new VcsAppendableHistoryPartnerAdapter(); + mySessionAdapter.reportCreatedEmptySession(new SvnHistorySession(myVcs, Collections.emptyList(), + myPath, SvnUtil.checkRepositoryVersion15(myVcs, version.getPath()), null, true)); + myProvider = (SvnHistoryProvider) myVcs.getVcsHistoryProvider(); + } + + @Override + public List step(StopMarker marker) throws VcsException { + List list = mySessionAdapter.getSession().getRevisionList(); + int limit = 0; + SVNRevision from; + if (list.isEmpty()) { + myRevisionTo = SVNRevision.create(1); + if (marker instanceof ToRevision) { + myRevisionTo = ((ToRevision)marker).getRevision(); + } + from = SVNRevision.create(myVersion.getPegRevision()); + } else { + from = ((SvnRevisionNumber) list.get(list.size() - 1).getRevisionNumber()).getRevision(); + } + if (marker instanceof Portion) { + limit = Portion.getOurStep(); + } + + VcsAppendableHistoryPartnerAdapter adapter = new VcsAppendableHistoryPartnerAdapter(); + myProvider.reportAppendableHistory(myPath, adapter, myRevisionTo, from, limit, myPeg, true); + final List newRevisions = adapter.getSession().getRevisionList(); + list.addAll(newRevisions); + return newRevisions; + } + + @Override + public void cutTo(VcsFileRevision endRevision) { + List list = mySessionAdapter.getSession().getRevisionList(); + int i = 0; + for (; i < list.size(); i++) { + final VcsFileRevision revision = list.get(i); + // it is same exactly object so ok if even equals is not overriden + if (endRevision.equals(revision)) { + break; + } + } + if (i < list.size()) { + final ArrayList copy = new ArrayList(list.subList(0, i + 1)); + list.clear(); + list.addAll(copy); + } + } + + @Override + public void dispose() { + if (myFileHistoryPanel != null) { + myFileHistoryPanel.dispose(); + } + } + + @Override + public JPanel createPanel() { + // todo do not forget to call dispose + // todo remove refresh + VcsAbstractHistorySession session = mySessionAdapter.getSession(); + List list = session.getRevisionList(); + VcsFileRevision last = null; + if (! list.isEmpty() && myRevisionTo.getNumber() > 0 && + myRevisionTo.equals(((SvnRevisionNumber) list.get(list.size() - 1).getRevisionNumber()).getRevision())) { + last = list.remove(list.size() - 1); + } + myFileHistoryPanel = new FileHistoryPanelImpl(myVcs, myPath, session, myProvider, null, new FileHistoryRefresherI() { + @Override + public void run(boolean isRefresh) { + //we will not refresh + } + + @Override + public boolean isFirstTime() { + return false; + } + }, true); + myFileHistoryPanel.setBottomRevisionForShowDiff(last); + myFileHistoryPanel.setBorder(BorderFactory.createLineBorder(UIUtil.getBorderColor())); + return myFileHistoryPanel; + } + } + + /*private static class HistoryAsBrowseChangesConflictSide implements AbstractConflictSide { + public HistoryAsBrowseChangesConflictSide(Project project, SVNConflictVersion version) { + //To change body of created methods use File | Settings | File Templates. + } + + @Override + public JPanel createPanel() { + return null; + } + }*/ +} diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/ConflictCreator.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/ConflictCreator.java new file mode 100644 index 000000000000..4584c814ce2f --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/ConflictCreator.java @@ -0,0 +1,201 @@ +/* + * 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 org.jetbrains.idea.svn; + +import com.intellij.openapi.diff.impl.patch.*; +import com.intellij.openapi.diff.impl.patch.formove.PatchApplier; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsConfiguration; +import com.intellij.openapi.vcs.VcsShowConfirmationOption; +import com.intellij.openapi.vcs.changes.LocalChangeList; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.Processor; +import com.intellij.util.containers.Convertor; +import junit.framework.Assert; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNStatusType; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 5/2/12 + * Time: 2:02 PM + */ +public class ConflictCreator { + private final Project myProject; + private final VirtualFile myTheirsDir; + private final VirtualFile myMineDir; + private final TreeConflictData.Data myData; + private final SvnClientRunner myClientRunner; + + public ConflictCreator(final Project project, + VirtualFile dir, + VirtualFile mineDir, + TreeConflictData.Data data, + final SvnClientRunner clientRunner) { + myProject = project; + myTheirsDir = dir; + myMineDir = mineDir; + myData = data; + myClientRunner = clientRunner; + } + + public void create() throws PatchSyntaxException, IOException { + // local changes, do not commit + for (TreeConflictData.FileData data : myData.getLeftFiles()) { + applyFileData(myMineDir, data); + } + + final PatchReader reader = new PatchReader(myData.getTheirsPatch()); + final List patches = reader.readAllPatches(); + final List filePatchList = new ArrayList(patches); + for (Iterator iterator = filePatchList.iterator(); iterator.hasNext(); ) { + final FilePatch patch = iterator.next(); + if (patch.isDeletedFile()) { + myClientRunner.delete(myTheirsDir, patch.getBeforeName()); + iterator.remove(); + } + } + + if (! filePatchList.isEmpty()) { + PatchApplier applier = new PatchApplier(myProject, myTheirsDir, filePatchList, (LocalChangeList) null, null, null); + applier.setIgnoreContentRootsCheck(); + applier.execute(); + Assert.assertEquals(0, applier.getRemainingPatches().size()); + } + + try { + Thread.sleep(10); + } + catch (InterruptedException e) { + // + } + + SvnVcs vcs = SvnVcs.getInstance(myProject); + + for (TextFilePatch patch : patches) { + if (patch.isNewFile() || ! Comparing.equal(patch.getAfterName(), patch.getBeforeName())) { + final String afterName = patch.getAfterName(); + final String[] parts = afterName.split("/"); + String subPath = ""; + for (String part : parts) { + final String path = subPath + part; + SVNInfo info = vcs.getInfo(new File(myTheirsDir.getPath(), path)); + if (info == null || info.getURL() == null) { + myClientRunner.add(myTheirsDir, path); + } + subPath += part + "/"; + } + if (! patch.isNewFile()) { + myClientRunner.delete(myTheirsDir, patch.getBeforeName()); + } + } + } + final IOException[] ioe = new IOException[1]; + VfsUtil.processFilesRecursively(myTheirsDir, new Processor() { + @Override + public boolean process(VirtualFile file) { + if (file.isDirectory() && file.getChildren().length == 0) { + try { + myClientRunner.delete(myTheirsDir, file.getPath()); + } + catch (IOException e) { + ioe[0] = e; + } + } + return true; + } + }, new Convertor() { + @Override + public Boolean convert(VirtualFile o) { + return ! SvnUtil.isAdminDirectory(o); + } + }); + /*FileUtil.processFilesRecursively(new File(myTheirsDir.getPath()), new Processor() { + @Override + public boolean process(File file) { + if (file.isDirectory() && file.listFiles().length == 0) { + try { + myClientRunner.delete(myTheirsDir, file.getPath()); + } + catch (IOException e) { + ioe[0] = e; + } + } + return true; + } + });*/ + if (ioe[0] != null) { + throw ioe[0]; + } + + // this will commit all patch changes + myClientRunner.checkin(myTheirsDir); + // this will create the conflict + myClientRunner.update(myMineDir); + myClientRunner.update(myTheirsDir); + } + + private void applyFileData(final VirtualFile root, final TreeConflictData.FileData fileData) throws IOException { + final File target = new File(root.getPath(), fileData.myRelativePath); + + // we dont apply properties changes fow now + if (SVNStatusType.STATUS_MISSING.equals(fileData.myNodeStatus)) { + // delete existing only from fs + FileUtil.delete(target); + return; + } else if (SVNStatusType.STATUS_UNVERSIONED.equals(fileData.myNodeStatus)) { + // create new unversioned + createFile(root, fileData, target); + return; + } else if (SVNStatusType.STATUS_ADDED.equals(fileData.myNodeStatus)) { + if (fileData.myCopyFrom != null) { + myClientRunner.copy(root, fileData.myCopyFrom, fileData.myRelativePath); + return; + } + createFile(root, fileData, target); + myClientRunner.add(root, fileData.myRelativePath); + return; + } else if (SVNStatusType.STATUS_DELETED.equals(fileData.myNodeStatus)) { + myClientRunner.delete(root, fileData.myRelativePath); + return; + } else if (SVNStatusType.STATUS_NORMAL.equals(fileData.myNodeStatus)) { + if (SVNStatusType.STATUS_MODIFIED.equals(fileData.myContentsStatus)) { + createFile(root, fileData, target); + return; + } + } + } + + private void createFile(final VirtualFile root, final TreeConflictData.FileData fileData, File target) throws IOException { + if (fileData.myIsDir) { + target.mkdirs(); + } else { + FileUtil.writeToFile(target, fileData.myContents); + } + } +} diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedTest.java index 6296242b8204..2aecb58360f1 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnChangesCorrectlyRefreshedTest.java @@ -50,32 +50,6 @@ public class SvnChangesCorrectlyRefreshedTest extends SvnTestCase { enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); } - private class SubTree { - private final VirtualFile myRootDir; - private VirtualFile mySourceDir; - private final VirtualFile myTargetDir; - - private VirtualFile myS1File; - private VirtualFile myS2File; - - private final List myTargetFiles; - private static final String ourS1Contents = "123"; - private static final String ourS2Contents = "abc"; - - private SubTree(final VirtualFile base) throws Exception { - myRootDir = createDirInCommand(base, "root"); - mySourceDir = createDirInCommand(myRootDir, "source"); - myS1File = createFileInCommand(mySourceDir, "s1.txt", ourS1Contents); - myS2File = createFileInCommand(mySourceDir, "s2.txt", ourS2Contents); - - myTargetDir = createDirInCommand(myRootDir, "target"); - myTargetFiles = new ArrayList(); - for (int i = 0; i < 10; i++) { - myTargetFiles.add(createFileInCommand(myTargetDir, "t" + (i+10) +".txt", ourS1Contents)); - } - } - } - private static void sleep(final int millis) { try { Thread.sleep(millis); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnClientRunner.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnClientRunner.java new file mode 100644 index 000000000000..42c62dd1e3e4 --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnClientRunner.java @@ -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 org.jetbrains.idea.svn; + +import com.intellij.execution.process.ProcessOutput; +import com.intellij.openapi.vfs.VirtualFile; + +import java.io.IOException; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 5/2/12 + * Time: 5:44 PM + */ +public interface SvnClientRunner { + ProcessOutput runSvn(final VirtualFile file, String... commandLine) throws IOException; + + void checkin(final VirtualFile file) throws IOException; + + void update(final VirtualFile file) throws IOException; + + void checkout(String repoUrl, VirtualFile file) throws IOException; + + void add(VirtualFile root, String path) throws IOException; + + void delete(VirtualFile root, String path) throws IOException; + + void copy(VirtualFile root, String path, String from) throws IOException; +} diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnClientRunnerImpl.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnClientRunnerImpl.java new file mode 100644 index 000000000000..f9e063356451 --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnClientRunnerImpl.java @@ -0,0 +1,73 @@ +/* + * 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 org.jetbrains.idea.svn; + +import com.intellij.execution.process.ProcessOutput; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.AbstractVcsTestCase; +import com.intellij.testFramework.TestClientRunner; + +import java.io.File; +import java.io.IOException; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 5/2/12 + * Time: 5:33 PM + */ +public class SvnClientRunnerImpl implements SvnClientRunner { + private final TestClientRunner myTestClientRunner; + + public SvnClientRunnerImpl(final TestClientRunner testClientRunner) { + myTestClientRunner = testClientRunner; + } + + @Override + public ProcessOutput runSvn(final VirtualFile file, String... commandLine) throws IOException { + return myTestClientRunner.runClient("svn", null, new File(file.getPath()), commandLine); + } + + @Override + public void checkin(final VirtualFile file) throws IOException { + AbstractVcsTestCase.verify(runSvn(file, "ci", "-m", "test")); + } + + @Override + public void update(final VirtualFile file) throws IOException { + AbstractVcsTestCase.verify(runSvn(file, "up", "--accept", "postpone")); + } + + @Override + public void checkout(final String repoUrl, final VirtualFile file) throws IOException { + AbstractVcsTestCase.verify(runSvn(file, "co", repoUrl, ".")); + } + + @Override + public void add(VirtualFile root, String path) throws IOException { + AbstractVcsTestCase.verify(runSvn(root, "add", path)); + } + + @Override + public void delete(VirtualFile root, String path) throws IOException { + AbstractVcsTestCase.verify(runSvn(root, "delete", path)); + } + + @Override + public void copy(VirtualFile root, String path, String from) throws IOException { + AbstractVcsTestCase.verify(runSvn(root, "copy", path, from)); + } +} diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnResolveTreeAcceptVariantsTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnResolveTreeAcceptVariantsTest.java new file mode 100644 index 000000000000..f8ee5005104e --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnResolveTreeAcceptVariantsTest.java @@ -0,0 +1,317 @@ +/* + * 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 org.jetbrains.idea.svn; + +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vcs.VcsConfiguration; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.TestClientRunner; +import com.intellij.util.Processor; +import junit.framework.Assert; +import org.jetbrains.idea.svn.treeConflict.SvnTreeConflictResolver; +import org.junit.Before; +import org.junit.Test; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNStatus; +import org.tmatesoft.svn.core.wc.SVNStatusType; + +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collection; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 5/3/12 + * Time: 6:14 PM + */ +public class SvnResolveTreeAcceptVariantsTest extends SvnTestCase { + private VirtualFile myTheirs; + private SvnClientRunnerImpl mySvnClientRunner; + private SvnVcs myVcs; + private VcsDirtyScopeManager myDirtyScopeManager; + private ChangeListManager myChangeListManager; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + disableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + + myTheirs = myTempDirFixture.findOrCreateDir("theirs"); + final TestClientRunner testClientRunner = new TestClientRunner(true, myClientBinaryPath); + mySvnClientRunner = new SvnClientRunnerImpl(testClientRunner); + clearWc(true); + + myVcs = SvnVcs.getInstance(myProject); + myDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject); + myChangeListManager = ChangeListManager.getInstance(myProject); + myTraceClient = true; + } + + private void clearWc(final boolean withSvn) { + myWorkingCopyDir.refresh(false, true); + /*VfsUtil.processFilesRecursively(myWorkingCopyDir, new Processor() { + @Override + public boolean process(VirtualFile file) { + if (myWorkingCopyDir.equals(file) || SvnUtil.isAdminDirectory(file)) return true; + FileUtil.delete(new File(file.getPath())); + return true; + } + }, new Convertor() { + @Override + public Boolean convert(VirtualFile o) { + return withSvn || ! SvnUtil.isAdminDirectory(o); + } + });*/ + clearDirInCommand(myWorkingCopyDir, new Processor() { + @Override + public boolean process(VirtualFile file) { + return withSvn || ! SvnUtil.isAdminDirectory(file); + } + }); + myWorkingCopyDir.refresh(false, true); + } + + @Test + public void testMineFull() throws Exception { + int cnt = 0; + myWorkingCopyDir = createDirInCommand(myWorkingCopyDir, "test--"); + myTheirs = createDirInCommand(myTheirs, "theirs--"); + // todo debug + //final TreeConflictData.Data data = TreeConflictData.DirToDir.MINE_EDIT_THEIRS_DELETE; + for (final TreeConflictData.Data data : TreeConflictData.ourAll) { + if (myTraceClient) { + System.out.println("========= TEST " + getTestName(data) + " ========="); + } + + myWorkingCopyDir = createDirInCommand(myWorkingCopyDir.getParent(), "test" + cnt); + myTheirs = createDirInCommand(myTheirs.getParent(), "theirs" + cnt); + mySvnClientRunner.checkout(myRepoUrl, myTheirs); + mySvnClientRunner.checkout(myRepoUrl, myWorkingCopyDir); + + createSubTree(); + final ConflictCreator creator = new ConflictCreator(myProject, myTheirs, myWorkingCopyDir, data, mySvnClientRunner); + creator.create(); + + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + + final String conflictFile = data.getConflictFile(); + + final File conflictIoFile = new File(myWorkingCopyDir.getPath(), conflictFile); + final FilePathImpl filePath = new FilePathImpl(conflictIoFile, conflictIoFile.isDirectory()); + final Change change = myChangeListManager.getChange(filePath); + Assert.assertNotNull(change); + Assert.assertTrue(change instanceof ConflictedSvnChange); + final SvnRevisionNumber committedRevision = + change.getBeforeRevision() != null ? (SvnRevisionNumber)change.getBeforeRevision().getRevisionNumber() : null; + //SvnRevisionNumber committedRevision = new SvnRevisionNumber(SVNRevision.create(cnt * 2 + 1)); + final SvnTreeConflictResolver resolver = new SvnTreeConflictResolver(myVcs, filePath, committedRevision, null); + + resolver.resolveSelectMineFull(((ConflictedSvnChange)change).getBeforeDescription()); + + myTheirs.refresh(false, true); + myWorkingCopyDir.refresh(false, true); + checkStatusesAfterMineFullResolve(data, conflictIoFile); + + ++ cnt; + } + } + + private void checkStatusesAfterMineFullResolve(TreeConflictData.Data data, File conflictIoFile) { + SVNStatus conflStatus = SvnUtil.getStatus(myVcs, conflictIoFile); + Assert.assertTrue(createTestFailedComment(data, conflictIoFile.getPath()) + " tree conflict resolved", + conflStatus.getTreeConflict() == null); + Collection leftFiles = data.getLeftFiles(); + for (TreeConflictData.FileData file : leftFiles) { + File exFile = new File(myWorkingCopyDir.getPath(), file.myRelativePath); + final SVNStatus status = SvnUtil.getStatus(myVcs, exFile); + boolean theirsExists = new File(myTheirs.getPath(), file.myRelativePath).exists(); + + if (SVNStatusType.STATUS_UNVERSIONED.equals(file.myNodeStatus)) { + Assert.assertTrue(createTestFailedComment(data, exFile.getPath()) + " (file exists)", exFile.exists()); + if (theirsExists) { + // should be deleted + Assert.assertTrue(createTestFailedComment(data, exFile.getPath()) + " (unversioned)", status == null || SVNStatusType.STATUS_DELETED.equals(status.getNodeStatus())); + } else { + // unversioned + Assert.assertTrue(createTestFailedComment(data, exFile.getPath()) + " (unversioned)", status == null || SVNStatusType.STATUS_UNVERSIONED.equals(status.getNodeStatus())); + } + } else if (SVNStatusType.STATUS_DELETED.equals(file.myNodeStatus)) { + Assert.assertTrue(createTestFailedComment(data, exFile.getPath()) + " (deleted status)", status != null && file.myNodeStatus.equals(status.getNodeStatus())); + } else if (SVNStatusType.STATUS_ADDED.equals(file.myNodeStatus)) { + Assert.assertTrue(createTestFailedComment(data, exFile.getPath()) + " (file exists)", exFile.exists()); + if (theirsExists) { + Assert.assertTrue(createTestFailedComment(data, exFile.getPath()) + " (added status)", status != null && SVNStatusType.STATUS_REPLACED.equals(status.getNodeStatus())); + } else { + Assert.assertTrue(createTestFailedComment(data, exFile.getPath()) + " (added status)", status != null && SVNStatusType.STATUS_ADDED.equals(status.getNodeStatus())); + } + } else { + if (SVNStatusType.STATUS_ADDED.equals(status.getNodeStatus())) { + // in theirs -> deleted + Assert.assertFalse(createTestFailedComment(data, file.myRelativePath) + " check deleted in theirs", theirsExists); + } else { + if (theirsExists) { + Assert.assertTrue(createTestFailedComment(data, exFile.getPath()) + " (normal node status)", status != null && SVNStatusType.STATUS_REPLACED.equals(status.getNodeStatus())); + } else { + Assert.assertTrue(createTestFailedComment(data, exFile.getPath()) + " (normal node status)", status != null && + (SVNStatusType.STATUS_NORMAL.equals(status.getNodeStatus()) || SVNStatusType.STATUS_MODIFIED.equals(status.getNodeStatus()))); + } + } + Assert.assertTrue(createTestFailedComment(data, exFile.getPath()) + " (modified text status)", status != null && file.myContentsStatus.equals(status.getContentsStatus())); + } + } + } + + private String createTestFailedComment(final TreeConflictData.Data data, final String path) { + return "Check failed for test: " + getTestName(data) + " and file: " + path + " in: " + myWorkingCopyDir.getPath(); + } + + @Test + public void testTheirsFull() throws Exception { + int cnt = 0; + myWorkingCopyDir = createDirInCommand(myWorkingCopyDir, "test--"); + myTheirs = createDirInCommand(myTheirs, "theirs--"); + // todo debug + //final TreeConflictData.Data data = TreeConflictData.FileToFile.MINE_MOVE_THEIRS_ADD; + for (final TreeConflictData.Data data : TreeConflictData.ourAll) { + if (myTraceClient) { + System.out.println("========= TEST " + getTestName(data) + " ========="); + } + + myWorkingCopyDir = createDirInCommand(myWorkingCopyDir.getParent(), "test" + cnt); + myTheirs = createDirInCommand(myTheirs.getParent(), "theirs" + cnt); + mySvnClientRunner.checkout(myRepoUrl, myTheirs); + mySvnClientRunner.checkout(myRepoUrl, myWorkingCopyDir); + + createSubTree(); + final ConflictCreator creator = new ConflictCreator(myProject, myTheirs, myWorkingCopyDir, data, mySvnClientRunner); + creator.create(); + + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + myDirtyScopeManager.markEverythingDirty(); + myChangeListManager.ensureUpToDate(false); + + final String conflictFile = data.getConflictFile(); + + final File conflictIoFile = new File(myWorkingCopyDir.getPath(), conflictFile); + final FilePathImpl filePath = new FilePathImpl(conflictIoFile, conflictIoFile.isDirectory()); + final Change change = myChangeListManager.getChange(filePath); + Assert.assertNotNull(change); + Assert.assertTrue(change instanceof ConflictedSvnChange); + final SvnRevisionNumber committedRevision = + change.getBeforeRevision() != null ? (SvnRevisionNumber)change.getBeforeRevision().getRevisionNumber() : null; + FilePath beforePath = null; + if (change.isMoved() || change.isRenamed()) { + beforePath = change.getBeforeRevision().getFile(); + } + //SvnRevisionNumber committedRevision = new SvnRevisionNumber(SVNRevision.create(cnt * 2 + 1)); + final SvnTreeConflictResolver resolver = new SvnTreeConflictResolver(myVcs, filePath, committedRevision, beforePath); + + resolver.resolveSelectTheirsFull(((ConflictedSvnChange) change).getBeforeDescription()); + + myTheirs.refresh(false, true); + myWorkingCopyDir.refresh(false, true); + VfsUtil.processFileRecursivelyWithoutIgnored(myTheirs, new Processor() { + @Override + public boolean process(VirtualFile file) { + final String relative = VfsUtil.getRelativePath(file, myTheirs, File.separatorChar); + File workingFile = new File(myWorkingCopyDir.getPath(), relative); + boolean exists = workingFile.exists(); + if (! exists) { + String[] excluded = data.getExcludeFromToTheirsCheck(); + if (excluded != null && Arrays.asList(excluded).contains(relative)) { + return true; + } + Assert.assertTrue("Check failed for test: " + getTestName(data) + " and file: " + relative + " in: " + myWorkingCopyDir.getPath(), + exists); + } + SVNInfo theirsInfo = myVcs.getInfo(new File(file.getPath())); + SVNInfo thisInfo = myVcs.getInfo(workingFile); + if (theirsInfo != null) { + Assert.assertEquals("Check failed for test: " + getTestName(data) + " and file: " + relative + " in: " + myWorkingCopyDir.getPath() + + ", theirs: " + theirsInfo.getRevision().getNumber() + ", mine: " + thisInfo.getRevision().getNumber(), + theirsInfo.getRevision().getNumber(), thisInfo.getRevision().getNumber()); + } + return true; + } + }); + ++ cnt; + } + } + + private String getTestName(final TreeConflictData.Data data) { + Class[] classes = TreeConflictData.class.getDeclaredClasses(); + for (Class aClass : classes) { + String s = testFields(data, aClass); + if (s != null) return aClass.getName() + "#" + s; + } + return null; + } + + private String testFields(TreeConflictData.Data data, final Class clazz) { + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + int modifiers = field.getModifiers(); + try { + if ((Modifier.STATIC & modifiers) != 0 && data == field.get(null)) { + return field.getName(); + } + } + catch (IllegalAccessException e) { + e.printStackTrace(); + return null; + } + } + return null; + } + + private void createSubTree() throws Exception { + enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); + + clearWc(false); + mySvnClientRunner.checkin(myWorkingCopyDir); + sleep(10); + final SubTree subTree = new SubTree(myWorkingCopyDir); + mySvnClientRunner.checkin(myWorkingCopyDir); + sleep(10); + mySvnClientRunner.update(myTheirs); + mySvnClientRunner.update(myWorkingCopyDir); + sleep(10); + + disableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + disableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); + } + + private static void sleep(final int millis) { + try { + Thread.sleep(millis); + } + catch (InterruptedException ignore) { } + } +} diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestCase.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestCase.java index ce90135fe141..4773f42f88ec 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestCase.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTestCase.java @@ -32,8 +32,10 @@ import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.VcsShowConfirmationOption; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.pending.MockChangeListManagerGate; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.AbstractJunitVcsTestCase; import com.intellij.testFramework.PlatformTestCase; +import com.intellij.testFramework.TestClientRunner; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; import com.intellij.testFramework.fixtures.TempDirTestFixture; import com.intellij.testFramework.vcs.MockChangelistBuilder; @@ -44,6 +46,7 @@ import org.junit.Before; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.List; /** @@ -56,6 +59,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { protected String myRepoUrl; private ChangeListManagerGate myGate; protected AtomicSectionsAware myRefreshCopiesStub; + private TestClientRunner myRunner; protected SvnTestCase() { PlatformTestCase.initPlatformLangPrefix(); @@ -63,6 +67,8 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { @Before public void setUp() throws Exception { + myRunner = createClientRunner(); + //System.setProperty("svnkit.wc.17", "false"); UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override @@ -151,7 +157,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { } protected ProcessOutput runSvn(String... commandLine) throws IOException { - return runClient("svn", null, myWcRoot, commandLine); + return myRunner.runClient("svn", null, myWcRoot, commandLine); } protected void enableSilentOperation(final VcsConfiguration.StandardConfirmation op) { @@ -191,4 +197,30 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { } }); } + + public class SubTree { + protected final VirtualFile myRootDir; + protected VirtualFile mySourceDir; + protected final VirtualFile myTargetDir; + + protected VirtualFile myS1File; + protected VirtualFile myS2File; + + protected final List myTargetFiles; + protected static final String ourS1Contents = "123"; + protected static final String ourS2Contents = "abc"; + + protected SubTree(final VirtualFile base) throws Exception { + myRootDir = createDirInCommand(base, "root"); + mySourceDir = createDirInCommand(myRootDir, "source"); + myS1File = createFileInCommand(mySourceDir, "s1.txt", ourS1Contents); + myS2File = createFileInCommand(mySourceDir, "s2.txt", ourS2Contents); + + myTargetDir = createDirInCommand(myRootDir, "target"); + myTargetFiles = new ArrayList(); + for (int i = 0; i < 10; i++) { + myTargetFiles.add(createFileInCommand(myTargetDir, "t" + (i+10) +".txt", ourS1Contents)); + } + } + } } diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTreeConflictDataTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTreeConflictDataTest.java new file mode 100644 index 000000000000..ba1c978b6548 --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnTreeConflictDataTest.java @@ -0,0 +1,828 @@ +/* + * 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 org.jetbrains.idea.svn; + +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vcs.FileStatus; +import com.intellij.openapi.vcs.VcsConfiguration; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.TestClientRunner; +import junit.framework.Assert; +import org.junit.Before; +import org.junit.Test; +import org.tmatesoft.svn.core.SVNNodeKind; +import org.tmatesoft.svn.core.internal.wc.SVNConflictVersion; +import org.tmatesoft.svn.core.wc.SVNConflictAction; +import org.tmatesoft.svn.core.wc.SVNOperation; +import org.tmatesoft.svn.core.wc.SVNTreeConflictDescription; + +import java.io.File; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 5/2/12 + * Time: 2:01 PM + */ +public class SvnTreeConflictDataTest extends SvnTestCase { + private VirtualFile myTheirs; + private SvnClientRunnerImpl mySvnClientRunner; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + disableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + + myTheirs = myTempDirFixture.findOrCreateDir("theirs"); + final TestClientRunner testClientRunner = new TestClientRunner(true, myClientBinaryPath); + mySvnClientRunner = new SvnClientRunnerImpl(testClientRunner); + mySvnClientRunner.checkout(myRepoUrl, myTheirs); + } + + @Test + public void testFile2File_MINE_UNV_THEIRS_ADD() throws Exception { + final ConflictCreator creator = new ConflictCreator(myProject, myTheirs, myWorkingCopyDir, + TreeConflictData.FileToFile.MINE_UNV_THEIRS_ADD, mySvnClientRunner); + creator.create(); + final String conflictFile = TreeConflictData.FileToFile.MINE_UNV_THEIRS_ADD.getConflictFile(); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + Assert.assertNull(beforeDescription.getSourceLeftVersion()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.FILE, version.getKind()); + } + + @Test + public void testFile2File_MINE_EDIT_THEIRS_DELETE() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToFile.MINE_EDIT_THEIRS_DELETE); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.DELETE, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNotNull(leftVersion); + Assert.assertEquals(SVNNodeKind.FILE, leftVersion.getKind()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.NONE, version.getKind()); + } + + private String createConflict(final TreeConflictData.Data data) throws Exception { + createSubTree(); + + final ConflictCreator creator = new ConflictCreator(myProject, myTheirs, myWorkingCopyDir, data, mySvnClientRunner); + creator.create(); + return data.getConflictFile(); + } + + @Test + public void testFile2File_MINE_DELETE_THEIRS_EDIT() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToFile.MINE_DELETE_THEIRS_EDIT); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.EDIT, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNotNull(leftVersion); + Assert.assertEquals(SVNNodeKind.FILE, leftVersion.getKind()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.FILE, version.getKind()); + } + + @Test + public void testFile2File_MINE_EDIT_THEIRS_MOVE() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToFile.MINE_EDIT_THEIRS_MOVE); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.DELETE, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNotNull(leftVersion); + Assert.assertEquals(SVNNodeKind.FILE, leftVersion.getKind()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.NONE, version.getKind()); + } + + @Test + public void testFile2File_MINE_UNV_THEIRS_MOVE() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToFile.MINE_UNV_THEIRS_MOVE); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.FILE, version.getKind()); + } + + @Test + public void testFile2File_MINE_MOVE_THEIRS_EDIT() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToFile.MINE_MOVE_THEIRS_EDIT); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.EDIT, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNotNull(leftVersion); + Assert.assertEquals(SVNNodeKind.FILE, leftVersion.getKind()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.FILE, version.getKind()); + } + + @Test + public void testFile2File_MINE_MOVE_THEIRS_ADD() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToFile.MINE_MOVE_THEIRS_ADD); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + //Assert.assertEquals(SVNNodeKind.FILE, leftVersion.getKind()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.FILE, version.getKind()); + } + + //---------------------------------- dirs -------------------------------------------------------- + @Test + public void testDir2Dir_MINE_UNV_THEIRS_ADD() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToDir.MINE_UNV_THEIRS_ADD); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.DIR, version.getKind()); + } + + @Test + public void testDir2Dir_MINE_EDIT_THEIRS_DELETE() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToDir.MINE_EDIT_THEIRS_DELETE); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.DELETE, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNotNull(leftVersion); + Assert.assertEquals(SVNNodeKind.DIR, leftVersion.getKind()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.NONE, version.getKind()); + } + + @Test + public void testDir2Dir_MINE_DELETE_THEIRS_EDIT() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToDir.MINE_DELETE_THEIRS_EDIT); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + final Change change = changeListManager.getChange(new FilePathImpl(new File(myWorkingCopyDir.getPath(), conflictFile), true)); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.EDIT, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNotNull(leftVersion); + Assert.assertEquals(SVNNodeKind.DIR, leftVersion.getKind()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.DIR, version.getKind()); + } + + @Test + public void testDir2Dir_MINE_EDIT_THEIRS_MOVE() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToDir.MINE_EDIT_THEIRS_MOVE); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.DELETE, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNotNull(leftVersion); + Assert.assertEquals(SVNNodeKind.DIR, leftVersion.getKind()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.NONE, version.getKind()); + } + + @Test + public void testDir2Dir_MINE_UNV_THEIRS_MOVE() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToDir.MINE_UNV_THEIRS_MOVE); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.DIR, version.getKind()); + } + + @Test + public void testDir2Dir_MINE_MOVE_THEIRS_EDIT() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToDir.MINE_MOVE_THEIRS_EDIT); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + final Change change = changeListManager.getChange(new FilePathImpl(new File(myWorkingCopyDir.getPath(), conflictFile), true)); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.EDIT, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNotNull(leftVersion); + Assert.assertEquals(SVNNodeKind.DIR, leftVersion.getKind()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.DIR, version.getKind()); + } + + @Test + public void testDir2Dir_MINE_MOVE_THEIRS_ADD() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToDir.MINE_MOVE_THEIRS_ADD); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + changeListManager.ensureUpToDate(false); + + final Change change = changeListManager.getChange(new FilePathImpl(new File(myWorkingCopyDir.getPath(), conflictFile), true)); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNotNull(leftVersion); + Assert.assertEquals(SVNNodeKind.DIR, leftVersion.getKind()); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.DIR, version.getKind()); + } + //--------------------------------- + @Test + public void testFile2Dir_MINE_UNV_THEIRS_ADD() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToDir.MINE_UNV_THEIRS_ADD); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.DIR, version.getKind()); + } + + @Test + public void testFile2Dir_MINE_ADD_THEIRS_ADD() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToDir.MINE_ADD_THEIRS_ADD); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.DIR, version.getKind()); + } + + @Test + public void testFile2Dir_MINE_UNV_THEIRS_MOVE() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToDir.MINE_UNV_THEIRS_MOVE); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.DIR, version.getKind()); + } + + @Test + public void testFile2Dir_MINE_ADD_THEIRS_MOVE() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToDir.MINE_ADD_THEIRS_MOVE); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.DIR, version.getKind()); + } + + @Test + public void testFile2Dir_MINE_MOVE_THEIRS_ADD() throws Exception { + final String conflictFile = createConflict(TreeConflictData.FileToDir.MINE_MOVE_THEIRS_ADD); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.DIR, version.getKind()); + } + //****************************************** + // dir -> file (mine, theirs) + @Test + public void testDir2File_MINE_UNV_THEIRS_ADD() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToFile.MINE_UNV_THEIRS_ADD); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.FILE, version.getKind()); + } + + @Test + public void testDir2File_MINE_ADD_THEIRS_ADD() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToFile.MINE_ADD_THEIRS_ADD); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.FILE, version.getKind()); + } + + @Test + public void testDir2File_MINE_UNV_THEIRS_MOVE() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToFile.MINE_UNV_THEIRS_MOVE); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.FILE, version.getKind()); + } + + @Test + public void testDir2File_MINE_ADD_THEIRS_MOVE() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToFile.MINE_ADD_THEIRS_MOVE); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.FILE, version.getKind()); + } + + @Test + public void testDir2File_MINE_MOVE_THEIRS_ADD() throws Exception { + final String conflictFile = createConflict(TreeConflictData.DirToFile.MINE_MOVE_THEIRS_ADD); + + ChangeListManager changeListManager = ChangeListManager.getInstance(myProject); + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VcsDirtyScopeManager.getInstance(myProject).markEverythingDirty(); + changeListManager.ensureUpToDate(false); + + VirtualFile vf = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(myWorkingCopyDir.getPath(), conflictFile)); + Assert.assertNotNull(vf); + final Change change = changeListManager.getChange(vf); + Assert.assertTrue(change instanceof ConflictedSvnChange); + SVNTreeConflictDescription beforeDescription = ((ConflictedSvnChange)change).getBeforeDescription(); + Assert.assertNotNull(beforeDescription); + + final SVNTreeConflictDescription afterDescription = ((ConflictedSvnChange)change).getAfterDescription(); + Assert.assertNull(afterDescription); + Assert.assertEquals(SVNOperation.UPDATE, beforeDescription.getOperation()); + Assert.assertEquals(SVNConflictAction.ADD, beforeDescription.getConflictAction()); + + Assert.assertTrue(beforeDescription.isTreeConflict()); + SVNConflictVersion leftVersion = beforeDescription.getSourceLeftVersion(); + Assert.assertNull(leftVersion); + + final SVNConflictVersion version = beforeDescription.getSourceRightVersion(); + Assert.assertNotNull(version); + Assert.assertEquals(SVNNodeKind.FILE, version.getKind()); + } + + private void createSubTree() throws Exception { + enableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + enableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); + + final SubTree subTree = new SubTree(myWorkingCopyDir); + mySvnClientRunner.checkin(myWorkingCopyDir); + sleep(10); + mySvnClientRunner.update(myTheirs); + mySvnClientRunner.update(myWorkingCopyDir); + sleep(10); + + disableSilentOperation(VcsConfiguration.StandardConfirmation.ADD); + disableSilentOperation(VcsConfiguration.StandardConfirmation.REMOVE); + } + + private static void sleep(final int millis) { + try { + Thread.sleep(millis); + } + catch (InterruptedException ignore) { } + } +} diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/TreeConflictData.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/TreeConflictData.java new file mode 100644 index 000000000000..7f2a52f31086 --- /dev/null +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/TreeConflictData.java @@ -0,0 +1,455 @@ +/* + * 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 org.jetbrains.idea.svn; + +import org.tmatesoft.svn.core.wc.SVNStatusType; + +import java.util.*; + +/** + * Created with IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 5/2/12 + * Time: 1:11 PM + */ +public interface TreeConflictData { + Data[] ourAll = new Data[] { + FileToFile.MINE_DELETE_THEIRS_EDIT, FileToFile.MINE_EDIT_THEIRS_DELETE, FileToFile.MINE_EDIT_THEIRS_MOVE, + FileToFile.MINE_UNV_THEIRS_ADD, FileToFile.MINE_UNV_THEIRS_MOVE, FileToFile.MINE_MOVE_THEIRS_EDIT, + FileToFile.MINE_MOVE_THEIRS_ADD, + + DirToDir.MINE_DELETE_THEIRS_EDIT, DirToDir.MINE_EDIT_THEIRS_DELETE, DirToDir.MINE_EDIT_THEIRS_MOVE, + DirToDir.MINE_UNV_THEIRS_ADD, DirToDir.MINE_UNV_THEIRS_MOVE, DirToDir.MINE_MOVE_THEIRS_EDIT, + DirToDir.MINE_MOVE_THEIRS_ADD, + + DirToFile.MINE_ADD_THEIRS_ADD, DirToFile.MINE_ADD_THEIRS_MOVE, DirToFile.MINE_UNV_THEIRS_ADD, + DirToFile.MINE_UNV_THEIRS_MOVE, DirToFile.MINE_MOVE_THEIRS_ADD, + + FileToDir.MINE_ADD_THEIRS_ADD, FileToDir.MINE_ADD_THEIRS_MOVE, FileToDir.MINE_UNV_THEIRS_ADD, + FileToDir.MINE_UNV_THEIRS_MOVE, FileToDir.MINE_MOVE_THEIRS_ADD}; + + interface FileToFile { + Data MINE_UNV_THEIRS_ADD = new Data("Index: added.txt\n" + + "===================================================================\n" + + "--- added.txt\t(revision )\n" + + "+++ added.txt\t(revision )\n" + + "@@ -0,0 +1,1 @@\n" + + "+added text\n" + + "\\ No newline at end of file\n", + "added.txt", new FileData[]{new FileData("added.txt", "unversioned text", SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + + Data MINE_EDIT_THEIRS_DELETE = new Data("Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source/s1.txt\t(revision 358)\n" + + "@@ -1,1 +0,0 @@\n" + + "-123\n" + + "\\ No newline at end of file\n", "root/source/s1.txt", + new FileData[] {new FileData("root/source/s1.txt", "1*2*3", SVNStatusType.STATUS_NORMAL, + SVNStatusType.STATUS_MODIFIED, SVNStatusType.STATUS_NORMAL, false)}); + Data MINE_DELETE_THEIRS_EDIT = new Data("Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source/s1.txt\t(revision )\n" + + "@@ -1,1 +1,1 @@\n" + + "-123\n" + + "\\ No newline at end of file\n" + + "+1*2*3\n" + + "\\ No newline at end of file\n", "root/source/s1.txt", + new FileData[] {new FileData("root/source/s1.txt", null, SVNStatusType.STATUS_DELETED, + SVNStatusType.STATUS_DELETED, SVNStatusType.STATUS_DELETED, false)}); + + Data MINE_EDIT_THEIRS_MOVE = new Data("Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source/s1renamed.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n", "root/source/s1.txt", + new FileData[] {new FileData("root/source/s1.txt", "1*2*3", SVNStatusType.STATUS_NORMAL, + SVNStatusType.STATUS_MODIFIED, SVNStatusType.STATUS_NORMAL, false)}); + + Data MINE_UNV_THEIRS_MOVE = new Data("Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source/s1renamed.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n", "root/source/s1renamed.txt", + new FileData[] {new FileData("root/source/s1renamed.txt", "1*2*3", SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + Data MINE_MOVE_THEIRS_EDIT = new Data("Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source/s1.txt\t(revision )\n" + + "@@ -1,1 +1,1 @@\n" + + "-123\n" + + "\\ No newline at end of file\n" + + "+1*2*3\n" + + // conflict would be marked by svn on s1.txt, but here we put s1moved.txt, for change list manager to find the change + "\\ No newline at end of file\n", "root/source/s1moved.txt", + new FileData[] {new FileData("root/source/s1moved.txt", null, SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_ADDED, SVNStatusType.STATUS_ADDED, false, "root/source/s1.txt"), + new FileData("root/source/s1.txt", null, SVNStatusType.STATUS_DELETED, + SVNStatusType.STATUS_DELETED, SVNStatusType.STATUS_DELETED, false)}); + Data MINE_MOVE_THEIRS_ADD = new Data("Index: root/source/s1moved.txt\n" + + "===================================================================\n" + + "--- root/source/s1moved.txt\t(revision )\n" + + "+++ root/source/s1moved.txt\t(revision )\n" + + "@@ -0,0 +1,1 @@\n" + + "+added text\n" + + "\\ No newline at end of file\n", + "root/source/s1moved.txt", + new FileData[] {new FileData("root/source/s1moved.txt", null, SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_ADDED, SVNStatusType.STATUS_ADDED, false, "root/source/s1.txt"), + new FileData("root/source/s1.txt", null, SVNStatusType.STATUS_DELETED, + SVNStatusType.STATUS_DELETED, SVNStatusType.STATUS_DELETED, false)}) { + @Override + protected void afterInit() { + setExcludeFromToTheirsCheck("root\\source\\s1.txt"); + } + }; + } + + interface DirToDir { + Data MINE_UNV_THEIRS_ADD = new Data("Index: addedDir/added.txt\n" + + "===================================================================\n" + + "--- addedDir/added.txt\t(revision )\n" + + "+++ addedDir/added.txt\t(revision )\n" + + "@@ -0,0 +1,1 @@\n" + + "+added text\n" + + "\\ No newline at end of file\n", + "addedDir", new FileData[]{new FileData("addedDir", null, SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + true), + new FileData("addedDir/unv.txt", "unversioned", SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + + Data MINE_EDIT_THEIRS_DELETE = new Data("Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source/s1.txt\t(revision 358)\n" + + "@@ -1,1 +0,0 @@\n" + + "-123\n" + + "\\ No newline at end of file\n" + + "Index: root/source/s2.txt\n" + + "===================================================================\n" + + "--- root/source/s2.txt\t(revision 358)\n" + + "+++ root/source/s2.txt\t(revision 358)\n" + + "@@ -1,1 +0,0 @@\n" + + "-abc\n" + + "\\ No newline at end of file\n", "root/source", + new FileData[] {new FileData("root/source/s1.txt", "1*2*3", SVNStatusType.STATUS_NORMAL, + SVNStatusType.STATUS_MODIFIED, SVNStatusType.STATUS_NORMAL, false)}); + Data MINE_DELETE_THEIRS_EDIT = new Data("Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source/s1.txt\t(revision )\n" + + "@@ -1,1 +1,1 @@\n" + + "-123\n" + + "\\ No newline at end of file\n" + + "+1*2*3\n" + + "\\ No newline at end of file\n", "root/source", + new FileData[] {new FileData("root/source", null, SVNStatusType.STATUS_DELETED, + SVNStatusType.STATUS_DELETED, SVNStatusType.STATUS_DELETED, true)}); + + Data MINE_EDIT_THEIRS_MOVE = new Data( + "Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source1/s1.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n" + + "Index: root/source/s2.txt\n" + + "===================================================================\n" + + "--- root/source/s2.txt\t(revision 358)\n" + + "+++ root/source1/s2.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n", + "root/source", + new FileData[] {new FileData("root/source/s1.txt", "1*2*3", SVNStatusType.STATUS_NORMAL, + SVNStatusType.STATUS_MODIFIED, SVNStatusType.STATUS_NORMAL, false)}); + + Data MINE_UNV_THEIRS_MOVE = new Data( + "Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source1/s1.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n" + + "Index: root/source/s2.txt\n" + + "===================================================================\n" + + "--- root/source/s2.txt\t(revision 358)\n" + + "+++ root/source1/s2.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n", "root/source1", + new FileData[] {new FileData("root/source1", null, SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + true), + new FileData("root/source1/unv.txt", "unversioned", SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + + Data MINE_MOVE_THEIRS_EDIT = new Data("Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source/s1.txt\t(revision )\n" + + "@@ -1,1 +1,1 @@\n" + + "-123\n" + + "\\ No newline at end of file\n" + + "+1*2*3\n" + + "\\ No newline at end of file\n", "root/source", + new FileData[] { + new FileData("root/sourceNew", null, SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_ADDED, SVNStatusType.STATUS_ADDED, true, "root/source"), + new FileData("root/source", null, SVNStatusType.STATUS_DELETED, + SVNStatusType.STATUS_DELETED, SVNStatusType.STATUS_DELETED, true)}); + Data MINE_MOVE_THEIRS_ADD = new Data("Index: root/sourceNew/added.txt\n" + + "===================================================================\n" + + "--- root/sourceNew/added.txt\t(revision )\n" + + "+++ root/sourceNew/added.txt\t(revision )\n" + + "@@ -0,0 +1,1 @@\n" + + "+added text\n" + + "\\ No newline at end of file\n", "root/sourceNew", + new FileData[] { + new FileData("root/sourceNew", null, SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_ADDED, SVNStatusType.STATUS_ADDED, true, "root/source"), + new FileData("root/source", null, SVNStatusType.STATUS_DELETED, + SVNStatusType.STATUS_DELETED, SVNStatusType.STATUS_DELETED, true)}) { + @Override + protected void afterInit() { + setExcludeFromToTheirsCheck("root\\source", "root\\source\\s1.txt", "root\\source\\s2.txt"); + } + }; + } + + // mine -> theirs + interface FileToDir { + Data MINE_UNV_THEIRS_ADD = new Data("Index: addedDir/added.txt\n" + + "===================================================================\n" + + "--- addedDir/added.txt\t(revision )\n" + + "+++ addedDir/added.txt\t(revision )\n" + + "@@ -0,0 +1,1 @@\n" + + "+added text\n" + + "\\ No newline at end of file\n", + "addedDir", new FileData[]{new FileData("addedDir", "unversioned", SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + + Data MINE_ADD_THEIRS_ADD = new Data("Index: addedDir/added.txt\n" + + "===================================================================\n" + + "--- addedDir/added.txt\t(revision )\n" + + "+++ addedDir/added.txt\t(revision )\n" + + "@@ -0,0 +1,1 @@\n" + + "+added text\n" + + "\\ No newline at end of file\n", + "addedDir", new FileData[]{new FileData("addedDir", "unversioned", SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + + Data MINE_UNV_THEIRS_MOVE = new Data( "Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source1/s1.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n" + + "Index: root/source/s2.txt\n" + + "===================================================================\n" + + "--- root/source/s2.txt\t(revision 358)\n" + + "+++ root/source1/s2.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n", + "root/source1", new FileData[]{new FileData("root/source1", "unversioned", SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + + Data MINE_ADD_THEIRS_MOVE = new Data( "Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source1/s1.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n" + + "Index: root/source/s2.txt\n" + + "===================================================================\n" + + "--- root/source/s2.txt\t(revision 358)\n" + + "+++ root/source1/s2.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n", + "root/source1", new FileData[]{new FileData("root/source1", "unversioned", SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + Data MINE_MOVE_THEIRS_ADD = new Data("Index: addedDir/added.txt\n" + + "===================================================================\n" + + "--- addedDir/added.txt\t(revision )\n" + + "+++ addedDir/added.txt\t(revision )\n" + + "@@ -0,0 +1,1 @@\n" + + "+added text\n" + + "\\ No newline at end of file\n", + "addedDir", new FileData[]{new FileData("addedDir", null, SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_ADDED, SVNStatusType.STATUS_ADDED, + false, "root/source/s1.txt"), + new FileData("root/source/s1.txt", null, SVNStatusType.STATUS_DELETED, + SVNStatusType.STATUS_DELETED, SVNStatusType.STATUS_DELETED, + false, null)}) { + @Override + protected void afterInit() { + setExcludeFromToTheirsCheck("root\\source\\s1.txt"); + } + }; + } + + // mine -> theirs + interface DirToFile { + Data MINE_UNV_THEIRS_ADD = new Data("Index: addedDir.txt\n" + + "===================================================================\n" + + "--- addedDir.txt\t(revision )\n" + + "+++ addedDir.txt\t(revision )\n" + + "@@ -0,0 +1,1 @@\n" + + "+added text\n" + + "\\ No newline at end of file\n", + "addedDir.txt", new FileData[]{new FileData("addedDir.txt", null, SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + true), + new FileData("addedDir.txt/unv.txt", "unversioned", SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + + Data MINE_ADD_THEIRS_ADD = new Data("Index: addedDir.txt\n" + + "===================================================================\n" + + "--- addedDir.txt\t(revision )\n" + + "+++ addedDir.txt\t(revision )\n" + + "@@ -0,0 +1,1 @@\n" + + "+added text\n" + + "\\ No newline at end of file\n", + "addedDir.txt", new FileData[]{new FileData("addedDir.txt", null, SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + true), + new FileData("addedDir.txt/unv.txt", "unversioned", SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + + Data MINE_UNV_THEIRS_MOVE = new Data( "Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source/s1renamed.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n" + + "\\ No newline at end of file\n", + "root/source/s1renamed.txt", new FileData[]{new FileData("root/source/s1renamed.txt", null, + SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + true), + new FileData("root/source/s1renamed.txt/file.txt", "unversioned", + SVNStatusType.STATUS_UNVERSIONED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + + Data MINE_ADD_THEIRS_MOVE = new Data( "Index: root/source/s1.txt\n" + + "===================================================================\n" + + "--- root/source/s1.txt\t(revision 358)\n" + + "+++ root/source/s1renamed.txt\t(revision )\n" + + "@@ -1,0 +1,0 @@\n" + + "\\ No newline at end of file\n", + "root/source/s1renamed.txt", new FileData[]{new FileData("root/source/s1renamed.txt", null, + SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + true), + new FileData("root/source/s1renamed.txt/file.txt", "unversioned", + SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_UNVERSIONED, SVNStatusType.STATUS_UNVERSIONED, + false)}); + + Data MINE_MOVE_THEIRS_ADD = new Data("Index: addedDir.txt\n" + + "===================================================================\n" + + "--- addedDir.txt\t(revision )\n" + + "+++ addedDir.txt\t(revision )\n" + + "@@ -0,0 +1,1 @@\n" + + "+added text\n" + + "\\ No newline at end of file\n", + "addedDir.txt", new FileData[]{new FileData("addedDir.txt", null, SVNStatusType.STATUS_ADDED, + SVNStatusType.STATUS_ADDED, SVNStatusType.STATUS_ADDED, true, "root/source"), + new FileData("root/source", null, SVNStatusType.STATUS_DELETED, + SVNStatusType.STATUS_DELETED, SVNStatusType.STATUS_DELETED, true)}) { + @Override + protected void afterInit() { + setExcludeFromToTheirsCheck("root\\source", "root\\source\\s1.txt", "root\\source\\s2.txt"); + } + }; + } + + class Data { + private final Collection myFileData; + private final String myPatch; + private final String myConflictFile; + private String[] myExcludeFromToTheirsCheck; + + public Data(String patch, String file, FileData... fileData) { + myConflictFile = file; + myFileData = new ArrayList(Arrays.asList(fileData)); + myPatch = patch; + afterInit(); + } + + protected void afterInit() { + } + + Collection getLeftFiles() { + return myFileData; + } + + String getTheirsPatch() { + return myPatch; + } + + public String getConflictFile() { + return myConflictFile; + } + + public String[] getExcludeFromToTheirsCheck() { + return myExcludeFromToTheirsCheck; + } + + public void setExcludeFromToTheirsCheck(String... excludeFromToTheirsCheck) { + myExcludeFromToTheirsCheck = excludeFromToTheirsCheck; + } + } + + class FileData { + public final String myRelativePath; + public final String myContents; + public final String myCopyFrom; + public final SVNStatusType myNodeStatus; + public final SVNStatusType myContentsStatus; + // not used for now + public final SVNStatusType myPropertiesStatus; + public boolean myIsDir; + + public FileData(String relativePath, + String contents, + SVNStatusType nodeStatus, + SVNStatusType contentsStatus, + SVNStatusType propertiesStatus, + boolean isDir) { + this(relativePath, contents, nodeStatus, contentsStatus, propertiesStatus, isDir, null); + } + + public FileData(String relativePath, + String contents, + SVNStatusType nodeStatus, + SVNStatusType contentsStatus, + SVNStatusType propertiesStatus, + boolean isDir, final String copyFrom) { + myRelativePath = relativePath; + myContents = contents; + myNodeStatus = nodeStatus; + myContentsStatus = contentsStatus; + myPropertiesStatus = propertiesStatus; + myIsDir = isDir; + myCopyFrom = copyFrom; + } + } +} diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnTestCase.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnTestCase.java index 1e5b2bf0def9..d303f7aab74f 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnTestCase.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnTestCase.java @@ -153,7 +153,7 @@ public abstract class SvnTestCase extends AbstractJunitVcsTestCase { } protected ProcessOutput runSvn(String... commandLine) throws IOException { - return runClient("svn", null, myWcRoot, commandLine); + return createClientRunner().runClient("svn", null, myWcRoot, commandLine); } protected void enableSilentOperation(final VcsConfiguration.StandardConfirmation op) {