diff --git a/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java b/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java index eafd649b3686..9f9e261657f3 100644 --- a/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java +++ b/platform/vcs-api/src/com/intellij/vcsUtil/VcsUtil.java @@ -310,6 +310,10 @@ public class VcsUtil { return getFilePath(new File(path)); } + public static FilePath getFilePath(@NotNull VirtualFile file) { + return VcsContextFactory.SERVICE.getInstance().createFilePathOn(file); + } + public static FilePath getFilePath(File file) { return VcsContextFactory.SERVICE.getInstance().createFilePathOn(file); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java index 490d8905a7e7..1e3fd7b1fb18 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java @@ -152,12 +152,6 @@ public class SvnUtil { return info != null && info.getURL() != null ? info.getURL().toString() : null; } - public static Map getLocationInfoForModule(final SvnVcs vcs, File path, ProgressIndicator progress) { - final LocationsCrawler crawler = new LocationsCrawler(vcs); - crawlWCRoots(vcs.getProject(), path, crawler, progress); - return crawler.getLocationInfos(); - } - public static void doLockFiles(Project project, final SvnVcs activeVcs, @NotNull final File[] ioFiles) throws VcsException { final String lockMessage; final boolean force; @@ -414,35 +408,6 @@ public class SvnUtil { return result; } - private static class LocationsCrawler implements SvnWCRootCrawler { - private final SvnVcs myVcs; - private final Map myLocations; - - public LocationsCrawler(SvnVcs vcs) { - myVcs = vcs; - myLocations = new HashMap(); - } - - public Map getLocationInfos() { - return Collections.unmodifiableMap(myLocations); - } - - public void handleWorkingCopyRoot(File root, ProgressIndicator progress) { - String oldText = null; - if (progress != null) { - oldText = progress.getText(); - progress.setText(SvnBundle.message("progress.text.discovering.location", root.getAbsolutePath())); - } - SVNInfo info = myVcs.getInfo(root); - if (info != null && info.getURL() != null) { - myLocations.put(info.getURL().toString(), info.getFile()); - } - if (progress != null) { - progress.setText(oldText); - } - } - } - @Nullable public static String getRepositoryUUID(final SvnVcs vcs, final File file) { final SVNInfo info = vcs.getInfo(file); @@ -569,6 +534,7 @@ public class SvnUtil { return repository.hasCapability(SVNCapability.MERGE_INFO); } catch (SVNException e) { + // TODO: Exception is thrown when url just not exist (was deleted, for instance) => and false is returned which seems not to be correct. return false; } finally { @@ -739,6 +705,23 @@ public class SvnUtil { } } + public static String getRelativeUrl(@NotNull String parentUrl, @NotNull String childUrl) { + return FileUtilRt.getRelativePath(parentUrl, childUrl, '/', true); + } + + public static String getRelativePath(@NotNull String parentPath, @NotNull String childPath) { + return FileUtilRt.getRelativePath(FileUtil.toSystemIndependentName(parentPath), FileUtil.toSystemIndependentName(childPath), '/'); + } + + public static String ensureStartSlash(@NotNull String path) { + return StringUtil.startsWithChar(path, '/') ? path : '/' + path; + } + + @NotNull + public static String join(@NotNull final String... parts) { + return StringUtil.join(parts, "/"); + } + public static String appendMultiParts(@NotNull final String base, @NotNull final String subPath) { if (StringUtil.isEmpty(subPath)) return base; final List parts = StringUtil.split(subPath.replace('\\', '/'), "/", true); @@ -764,9 +747,7 @@ public class SvnUtil { @Nullable final SVNRevision revision, @Nullable final SVNRevision pegRevision) throws VcsException { - ClientFactory factory = target.isFile() ? vcs.getFactory(target.getFile()) : vcs.getFactory(); - - return factory.createContentClient().getContent(target, revision, pegRevision); + return vcs.getFactory(target).createContentClient().getContent(target, revision, pegRevision); } public static boolean hasDefaultPort(@NotNull SVNURL result) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java index bcfcba50b87a..1976a31e311d 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/commandLine/SvnBindException.java @@ -15,9 +15,11 @@ */ package org.jetbrains.idea.svn.commandLine; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.util.LineSeparator; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,6 +40,9 @@ import java.util.regex.Matcher; */ public class SvnBindException extends VcsException { + public static final int ERROR_BASE = 120000; + public static final int CATEGORY_SIZE = 5000; + private Map errors = new HashMap(); public SvnBindException(String message) { @@ -67,6 +72,21 @@ public class SvnBindException extends VcsException { return errors.containsKey(error.getCode()); } + public boolean containsCategory(int category) { + final int categoryCode = getCategoryCode(category); + + return ContainerUtil.exists(errors.keySet(), new Condition() { + @Override + public boolean value(Integer code) { + return getCategoryCode(code) == categoryCode; + } + }); + } + + private static int getCategoryCode(int category) { + return (category - ERROR_BASE) / CATEGORY_SIZE; + } + private void parseErrors(@NotNull String message) { Matcher matcher = SvnUtil.ERROR_PATTERN.matcher(message); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java index 32a73ac8d0e5..ae9fd514de79 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RepositoryBrowserDialog.java @@ -35,7 +35,6 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vcs.*; -import com.intellij.openapi.vcs.actions.VcsContextFactory; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.ChangesUtil; import com.intellij.openapi.vcs.changes.ContentRevision; @@ -47,6 +46,7 @@ import com.intellij.ui.PopupHandler; import com.intellij.util.IconUtil; import com.intellij.util.NotNullFunction; import com.intellij.util.WaitForProgressToShow; +import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -57,7 +57,6 @@ import org.jetbrains.idea.svn.dialogs.browser.*; import org.jetbrains.idea.svn.dialogs.browserCache.Expander; import org.jetbrains.idea.svn.dialogs.browserCache.KeepingExpandedExpander; import org.jetbrains.idea.svn.dialogs.browserCache.SyntheticWorker; -import org.jetbrains.idea.svn.history.SvnHistoryProvider; import org.jetbrains.idea.svn.history.SvnRepositoryLocation; import org.jetbrains.idea.svn.status.SvnDiffEditor; import org.tmatesoft.svn.core.*; @@ -355,9 +354,10 @@ public class RepositoryBrowserDialog extends DialogWrapper { } boolean isDirectory = node.getUserObject() instanceof SVNURL || (node.getSVNDirEntry() != null && node.getSVNDirEntry().getKind() == SVNNodeKind.DIR); - AbstractVcsHelper.getInstance(myProject).showFileHistory(new SvnHistoryProvider(myVCS), - VcsContextFactory.SERVICE.getInstance().createFilePathOnNonLocal(node.getURL().toDecodedString(), isDirectory), - myVCS, node.getURL().toDecodedString()); + String url = node.getURL().toDecodedString(); + + AbstractVcsHelper.getInstance(myProject) + .showFileHistory(myVCS.getVcsHistoryProvider(), VcsUtil.getFilePathOnNonLocal(url, isDirectory), myVCS, url); node.reload(false); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/BunchFactory.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/BunchFactory.java index 1e4760b487a4..426c760836b6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/BunchFactory.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/BunchFactory.java @@ -17,8 +17,8 @@ package org.jetbrains.idea.svn.history; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; -import org.tmatesoft.svn.core.SVNException; import java.util.ArrayList; import java.util.Iterator; @@ -57,7 +57,7 @@ public class BunchFactory { myYoungest = -1; } - public List goBack(final int bunchSize, final Ref myYoungestRead) throws SVNException { + public List goBack(final int bunchSize, final Ref myYoungestRead) throws VcsException { execute(bunchSize); myYoungestRead.set(myLiveProvider.isEarliestRevisionWasAccessed()); return new ArrayList(myResult); @@ -78,7 +78,7 @@ public class BunchFactory { myYoungest = subList.get(subList.size() - 1).getNumber(); } - private void execute(final int bunchSize) throws SVNException { + private void execute(final int bunchSize) throws VcsException { myBunchSize = bunchSize; myResult.clear(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/BunchProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/BunchProvider.java index 482cce94d97f..60944744a00e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/BunchProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/BunchProvider.java @@ -15,8 +15,8 @@ */ package org.jetbrains.idea.svn.history; +import com.intellij.openapi.vcs.VcsException; import org.jetbrains.annotations.Nullable; -import org.tmatesoft.svn.core.SVNException; public interface BunchProvider { long getEarliestRevision(); @@ -29,6 +29,6 @@ public interface BunchProvider { */ @Nullable Fragment getEarliestBunchInInterval(final long earliestRevision, final long oldestRevision, final int desirableSize, - final boolean includeYoungest, final boolean includeOldest) throws SVNException; + final boolean includeYoungest, final boolean includeOldest) throws VcsException; boolean isEmpty(); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CachedProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CachedProvider.java index 294a2cd29a30..b261ff190e49 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CachedProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CachedProvider.java @@ -15,10 +15,10 @@ */ package org.jetbrains.idea.svn.history; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.committed.ChangesBunch; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import org.jetbrains.annotations.Nullable; -import org.tmatesoft.svn.core.SVNException; import java.util.ArrayList; import java.util.Iterator; @@ -86,7 +86,7 @@ public abstract class CachedProvider implements BunchProvider { @Nullable public Fragment getEarliestBunchInInterval(final long earliestRevision, final long oldestRevision, final int desirableSize, - final boolean includeYoungest, final boolean includeOldest) throws SVNException { + final boolean includeYoungest, final boolean includeOldest) throws VcsException { if ((earliestRevision > getEarliestRevision()) || (earliestRevision == -1)) { if (myAlreadyReaded == null) { return null; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java index a044fedf9678..567c21f694e6 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/CmdHistoryClient.java @@ -7,11 +7,9 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; import org.jetbrains.idea.svn.commandLine.CommandExecutor; import org.jetbrains.idea.svn.commandLine.CommandUtil; +import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.jetbrains.idea.svn.commandLine.SvnCommandName; -import org.tmatesoft.svn.core.ISVNLogEntryHandler; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNLogEntry; -import org.tmatesoft.svn.core.SVNLogEntryPath; +import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; @@ -52,7 +50,7 @@ public class CmdHistoryClient extends BaseSvnClient implements HistoryClient { parseOutput(command, handler); } catch (SVNException e) { - throw new VcsException(e); + throw new SvnBindException(e); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LiveProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LiveProvider.java index 573a22647fdb..b50a6de8d970 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LiveProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LiveProvider.java @@ -20,12 +20,13 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; +import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.tmatesoft.svn.core.SVNErrorCode; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.SVNRevision; import java.util.Iterator; @@ -54,7 +55,7 @@ public class LiveProvider implements BunchProvider { } public Fragment getEarliestBunchInInterval(final long earliestRevision, final long oldestRevision, final int desirableSize, - final boolean includeYoungest, final boolean includeOldest) throws SVNException { + final boolean includeYoungest, final boolean includeOldest) throws VcsException { return getEarliestBunchInIntervalImpl(earliestRevision, oldestRevision, desirableSize, includeYoungest, includeOldest, earliestRevision); } @@ -62,14 +63,14 @@ public class LiveProvider implements BunchProvider { final long oldestRevision, final int desirableSize, final boolean includeYoungest, - final boolean includeOldest, final long earliestToTake) throws SVNException { + final boolean includeOldest, final long earliestToTake) throws VcsException { if ((myEarliestRevisionWasAccessed) || ((oldestRevision == myYoungestRevision) && ((! includeYoungest) || (! includeOldest)))) { return null; } final SVNRevision youngRevision = (earliestRevision == -1) ? SVNRevision.HEAD : SVNRevision.create(earliestRevision); final Ref> refToList = new Ref>(); - final Ref exceptionRef = new Ref(); + final Ref exceptionRef = new Ref(); final Runnable loader = new Runnable() { public void run() { @@ -77,7 +78,7 @@ public class LiveProvider implements BunchProvider { refToList.set( myLoader.loadInterval(youngRevision, SVNRevision.create(oldestRevision), desirableSize, includeYoungest, includeOldest)); } - catch (SVNException e) { + catch (VcsException e) { exceptionRef.set(e); } } @@ -98,13 +99,13 @@ public class LiveProvider implements BunchProvider { }, SvnBundle.message("progress.live.provider.loading.revisions.text"), false, myVcs.getProject()); } - if (exceptionRef.get() != null) { - final SVNException e = exceptionRef.get(); - if (SVNErrorCode.FS_NOT_FOUND.equals(e.getErrorMessage().getErrorCode())) { + if (!exceptionRef.isNull()) { + final VcsException e = exceptionRef.get(); + if (isElementNotFound(e)) { // occurs when target URL is deleted in repository // try to find latest existent revision. expensive ... final LatestExistentSearcher searcher = new LatestExistentSearcher(oldestRevision, myYoungestRevision, (oldestRevision != 0), - myVcs, SVNURL.parseURIEncoded(myLocation.getURL())); + myVcs, myLocation.toSvnUrl()); final long existent = searcher.getLatestExistent(); if ((existent == -1) || (existent == earliestRevision)) { myEarliestRevisionWasAccessed = true; @@ -132,6 +133,10 @@ public class LiveProvider implements BunchProvider { return new Fragment(Origin.LIVE, list, true, true, null); } + private static boolean isElementNotFound(@NotNull VcsException e) { + return e instanceof SvnBindException && ((SvnBindException)e).contains(SVNErrorCode.FS_NOT_FOUND); + } + public boolean isEarliestRevisionWasAccessed() { return myEarliestRevisionWasAccessed; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LocationDetector.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LocationDetector.java deleted file mode 100644 index 55cc740e17a8..000000000000 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/LocationDetector.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2000-2009 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.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.ProjectLevelVcsManager; -import com.intellij.openapi.vcs.actions.VcsContextFactory; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.NotNullFunction; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.idea.svn.SvnUtil; -import org.jetbrains.idea.svn.SvnVcs; -import org.tmatesoft.svn.core.internal.util.SVNPathUtil; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -public class LocationDetector { - private final Map myMap; - - public LocationDetector(final SvnVcs vcs) { - myMap = new HashMap(); - - final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); - final VirtualFile[] roots = ProjectLevelVcsManager.getInstance(vcs.getProject()).getRootsUnderVcs(vcs); - - for (VirtualFile root : roots) { - myMap.putAll(SvnUtil.getLocationInfoForModule(vcs, new File(root.getPath()), progress)); - } - } - - @Nullable - public FilePath crawlForPath(final String fullPath, final NotNullFunction detector) { - for (Map.Entry entry : myMap.entrySet()) { - final String url = entry.getKey(); - if (SVNPathUtil.isAncestor(url, fullPath)) { - return filePathByUrlAndPath(fullPath, url, entry.getValue().getAbsolutePath(), detector); - } - } - return null; - } - - static FilePath filePathByUrlAndPath(final String longPath, final String parentUrl, final String parentPath, final NotNullFunction detector) { - final String relPath = longPath.substring(parentUrl.length()); - final File localFile = new File(parentPath, relPath); - return VcsContextFactory.SERVICE.getInstance().createFilePathOn(localFile, detector); - } -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/MergeSourceHierarchyBuilder.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/MergeSourceHierarchyBuilder.java new file mode 100644 index 000000000000..f0a77198925d --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/MergeSourceHierarchyBuilder.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2014 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.util.Pair; +import com.intellij.util.Consumer; +import com.intellij.util.ThrowableConsumer; +import org.jetbrains.annotations.NotNull; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNLogEntry; + +import java.util.List; + +/** +* @author Konstantin Kolosovsky. +*/ +public class MergeSourceHierarchyBuilder implements ThrowableConsumer, SVNException> { + + private TreeStructureNode myCurrentHierarchy; + @NotNull private final Consumer> myConsumer; + + public MergeSourceHierarchyBuilder(@NotNull Consumer> consumer) { + myConsumer = consumer; + } + + public void consume(Pair svnLogEntryIntegerPair) throws SVNException { + final SVNLogEntry logEntry = svnLogEntryIntegerPair.getFirst(); + final Integer mergeLevel = svnLogEntryIntegerPair.getSecond(); + + if (mergeLevel < 0) { + if (myCurrentHierarchy != null) { + myConsumer.consume(myCurrentHierarchy); + } + if (logEntry.hasChildren()) { + myCurrentHierarchy = new TreeStructureNode(logEntry); + } else { + // just pass + myCurrentHierarchy = null; + myConsumer.consume(new TreeStructureNode(logEntry)); + } + } else { + addToLevel(myCurrentHierarchy, logEntry, mergeLevel); + } + } + + public void finish() { + if (myCurrentHierarchy != null) { + myConsumer.consume(myCurrentHierarchy); + } + } + + private static void addToLevel(final TreeStructureNode tree, final SVNLogEntry entry, final int left) { + assert tree != null; + if (left == 0) { + tree.add(entry); + } else { + final List> children = tree.getChildren(); + assert ! children.isEmpty(); + addToLevel(children.get(children.size() - 1), entry, left - 1); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/ShowHideMergePanelAction.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/ShowHideMergePanelAction.java new file mode 100644 index 000000000000..570167f7f90f --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/ShowHideMergePanelAction.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2014 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.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.actionSystem.ToggleAction; +import com.intellij.openapi.vcs.changes.committed.ChangeListFilteringStrategy; +import com.intellij.openapi.vcs.changes.committed.DecoratorManager; +import icons.SvnIcons; +import org.jetbrains.idea.svn.SvnBundle; + +/** +* @author Konstantin Kolosovsky. +*/ +public class ShowHideMergePanelAction extends ToggleAction { + + private final DecoratorManager myManager; + private final ChangeListFilteringStrategy myStrategy; + private boolean myIsSelected; + + public ShowHideMergePanelAction(final DecoratorManager manager, final ChangeListFilteringStrategy strategy) { + myManager = manager; + myStrategy = strategy; + } + + @Override + public void update(final AnActionEvent e) { + super.update(e); + final Presentation presentation = e.getPresentation(); + presentation.setIcon(SvnIcons.ShowIntegratedFrom); + presentation.setText(SvnBundle.message("committed.changes.action.enable.merge.highlighting")); + presentation.setDescription(SvnBundle.message("committed.changes.action.enable.merge.highlighting.description.text")); + } + + public boolean isSelected(final AnActionEvent e) { + return myIsSelected; + } + + public void setSelected(final AnActionEvent e, final boolean state) { + myIsSelected = state; + if (state) { + myManager.setFilteringStrategy(myStrategy); + } else { + myManager.removeFilteringStrategy(myStrategy.getKey()); + } + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SingleCommittedListProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SingleCommittedListProvider.java new file mode 100644 index 000000000000..e62b8e041623 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SingleCommittedListProvider.java @@ -0,0 +1,179 @@ +/* + * Copyright 2000-2014 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.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ContentRevision; +import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.vcsUtil.VcsUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.*; +import org.jetbrains.idea.svn.commandLine.SvnBindException; +import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; + +import java.io.File; + +/** +* @author Konstantin Kolosovsky. +*/ +public class SingleCommittedListProvider { + + private static final Logger LOG = Logger.getInstance(SingleCommittedListProvider.class); + + @NotNull private final SvnVcs myVcs; + @NotNull private final Project myProject; + @NotNull private final VirtualFile file; + @NotNull private final VcsRevisionNumber number; + private SvnChangeList[] changeList; + private SVNRevision revisionBefore; + private SVNURL repositoryUrl; + private SVNURL svnRootUrl; + private SvnRepositoryLocation svnRootLocation; + private String repositoryRelativeUrl; + private FilePath filePath; + + public SingleCommittedListProvider(@NotNull SvnVcs vcs, @NotNull VirtualFile file, @NotNull VcsRevisionNumber number) { + myVcs = vcs; + myProject = vcs.getProject(); + this.file = file; + this.number = number; + } + + public Pair run() throws VcsException { + Pair result = null; + + if (setup()) { + calculate(); + result = Pair.create(changeList[0], filePath); + } + + return result; + } + + private boolean setup() { + boolean result = false; + + RootUrlInfo rootUrlInfo = myVcs.getSvnFileUrlMapping().getWcRootForFilePath(new File(file.getPath())); + if (rootUrlInfo != null) { + changeList = new SvnChangeList[1]; + revisionBefore = ((SvnRevisionNumber)number).getRevision(); + repositoryUrl = rootUrlInfo.getRepositoryUrlUrl(); + svnRootUrl = rootUrlInfo.getAbsoluteUrlAsUrl(); + svnRootLocation = new SvnRepositoryLocation(rootUrlInfo.getAbsoluteUrl()); + repositoryRelativeUrl = SvnUtil.ensureStartSlash(SvnUtil.join( + SvnUtil.getRelativeUrl(repositoryUrl.toDecodedString(), svnRootUrl.toDecodedString()), + SvnUtil.getRelativePath(rootUrlInfo.getPath(), file.getPath()))); + + filePath = VcsUtil.getFilePath(file); + + result = true; + } + + return result; + } + + private void calculate() throws VcsException { + // TODO: Seems that filePath detection could be replaced with "svn info -r " - and not call + // TODO: "svn log -r HEAD:" and track copies manually (which also is not correct for all cases). + if (!searchForUrl(svnRootUrl) && !(hasAccess(repositoryUrl) && searchForUrl(repositoryUrl))) { + filePath = searchFromHead(svnRootUrl); + } + else { + if (changeList[0].getChanges().size() == 1) { + final ContentRevision afterRevision = changeList[0].getChanges().iterator().next().getAfterRevision(); + + filePath = afterRevision != null ? afterRevision.getFile() : filePath; + } + else { + final Change targetChange = changeList[0].getByPath(repositoryRelativeUrl); + + filePath = targetChange == null ? searchFromHead(svnRootUrl) : filePath; + } + } + } + + private boolean hasAccess(@NotNull SVNURL url) { + return SvnAuthenticationNotifier.passiveValidation(myProject, url); + } + + // return changed path, if any + private FilePath searchFromHead(@NotNull SVNURL url) throws VcsException { + final SvnCopyPathTracker pathTracker = new SvnCopyPathTracker(repositoryUrl.toDecodedString(), repositoryRelativeUrl); + SvnTarget target = SvnTarget.fromURL(url); + + myVcs.getFactory(target).createHistoryClient().doLog(target, SVNRevision.HEAD, revisionBefore, false, true, false, 0, null, + new ISVNLogEntryHandler() { + public void handleLogEntry(SVNLogEntry logEntry) { + checkDisposed(); + // date could be null for lists where there are paths that user has no rights to observe + if (logEntry.getDate() != null) { + pathTracker.accept(logEntry); + if (logEntry.getRevision() == revisionBefore.getNumber()) { + changeList[0] = createChangeList(logEntry); + } + } + } + } + ); + + FilePath path = pathTracker.getFilePath(myVcs); + return path == null ? filePath : path; + } + + @NotNull + private SvnChangeList createChangeList(@NotNull SVNLogEntry logEntry) { + return new SvnChangeList(myVcs, svnRootLocation, logEntry, repositoryUrl.toDecodedString()); + } + + private void checkDisposed() { + if (myProject.isDisposed()) { + throw new ProcessCanceledException(); + } + } + + private boolean searchForUrl(@NotNull SVNURL url) throws VcsException { + ISVNLogEntryHandler handler = new ISVNLogEntryHandler() { + public void handleLogEntry(SVNLogEntry logEntry) { + checkDisposed(); + // date could be null for lists where there are paths that user has no rights to observe + if (logEntry.getDate() != null) { + changeList[0] = createChangeList(logEntry); + } + } + }; + + SvnTarget target = SvnTarget.fromURL(url); + try { + myVcs.getFactory(target).createHistoryClient().doLog(target, revisionBefore, revisionBefore, false, true, false, 1, null, handler); + } + catch (SvnBindException e) { + LOG.info(e); + if (!e.containsCategory(SVNErrorCode.FS_CATEGORY)) { + throw e; + } + } + return changeList[0] != null; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java index a8d76b11dc56..f8b0717502ac 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedChangesProvider.java @@ -16,19 +16,20 @@ package org.jetbrains.idea.svn.history; -import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; -import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.ThrowableComputable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.*; -import com.intellij.openapi.vcs.changes.Change; -import com.intellij.openapi.vcs.changes.ContentRevision; -import com.intellij.openapi.vcs.changes.committed.*; +import com.intellij.openapi.vcs.changes.committed.DecoratorManager; +import com.intellij.openapi.vcs.changes.committed.VcsCommittedListsZipper; +import com.intellij.openapi.vcs.changes.committed.VcsCommittedViewAuxiliary; +import com.intellij.openapi.vcs.changes.committed.VcsConfigurationChangeListener; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; import com.intellij.openapi.vcs.versionBrowser.ChangesBrowserSettingsEditor; @@ -38,23 +39,19 @@ import com.intellij.util.AsynchConsumer; import com.intellij.util.Consumer; import com.intellij.util.PairConsumer; import com.intellij.util.ThrowableConsumer; -import com.intellij.util.containers.MultiMap; import com.intellij.util.messages.MessageBusConnection; -import icons.SvnIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; import org.jetbrains.idea.svn.actions.ConfigureBranchesAction; +import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.tmatesoft.svn.core.*; -import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.io.SVNRepository; -import org.tmatesoft.svn.core.wc.SVNInfo; import org.tmatesoft.svn.core.wc.SVNLogClient; import org.tmatesoft.svn.core.wc.SVNRevision; import java.io.DataInput; import java.io.DataOutput; -import java.io.File; import java.io.IOException; import java.util.*; @@ -66,17 +63,15 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi private final SvnVcs myVcs; private final MessageBusConnection myConnection; private MergeInfoUpdatesListener myMergeInfoUpdatesListener; - private final MyZipper myZipper; + private final SvnCommittedListsZipper myZipper; public final static int VERSION_WITH_COPY_PATHS_ADDED = 2; public final static int VERSION_WITH_REPLACED_PATHS = 3; - private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.history.SvnCommittedChangesProvider"); - public SvnCommittedChangesProvider(final Project project) { myProject = project; myVcs = SvnVcs.getInstance(myProject); - myZipper = new MyZipper(); + myZipper = new SvnCommittedListsZipper(myVcs); myConnection = myProject.getMessageBus().connect(); @@ -110,17 +105,12 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi @Nullable public RepositoryLocation getLocationFor(final FilePath root) { - final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); final String url = SvnUtil.getExactLocation(myVcs, root.getIOFile()); return url == null ? null : new SvnRepositoryLocation(url); } public RepositoryLocation getLocationFor(final FilePath root, final String repositoryPath) { - if (repositoryPath == null) { - return getLocationFor(root); - } - - return new SvnLoadingRepositoryLocation(repositoryPath, myVcs); + return repositoryPath == null ? getLocationFor(root) : new SvnRepositoryLocation(repositoryPath); } @Nullable @@ -128,49 +118,6 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi return myZipper; } - private class MyZipper implements VcsCommittedListsZipper { - public Pair, List> groupLocations(final List in) { - final List groups = new ArrayList(); - final List singles = new ArrayList(); - - final MultiMap map = new MultiMap(); - - for (RepositoryLocation location : in) { - final SvnRepositoryLocation svnLocation = (SvnRepositoryLocation) location; - final String url = svnLocation.getURL(); - - final SVNURL root = SvnUtil.getRepositoryRoot(myVcs, url); - if (root == null) { - // should not occur - LOG.info("repository root not found for location:"+ location.toPresentableString()); - singles.add(location); - } else { - map.putValue(root, svnLocation); - } - } - - final Set keys = map.keySet(); - for (SVNURL key : keys) { - final Collection repositoryLocations = map.get(key); - if (repositoryLocations.size() == 1) { - singles.add(repositoryLocations.iterator().next()); - } else { - final SvnRepositoryLocationGroup group = new SvnRepositoryLocationGroup(key, repositoryLocations); - groups.add(group); - } - } - return new Pair, List>(groups, singles); - } - - public CommittedChangeList zip(final RepositoryLocationGroup group, final List lists) { - return new SvnChangeList(lists, new SvnRepositoryLocation(group.toPresentableString())); - } - - public long getNumber(final CommittedChangeList list) { - return list.getNumber(); - } - } - public void loadCommittedChanges(ChangeBrowserSettings settings, RepositoryLocation location, int maxCount, @@ -178,30 +125,10 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi throws VcsException { try { final SvnRepositoryLocation svnLocation = (SvnRepositoryLocation) location; - final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); - if (progress != null) { - progress.setText(SvnBundle.message("progress.text.changes.collecting.changes")); - progress.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", location)); - } - - // TODO: Implement this with command line - final String repositoryRoot; - SVNRepository repository = null; - try { - repository = myVcs.createRepository(svnLocation.getURL()); - repositoryRoot = repository.getRepositoryRoot(true).toString(); - } - catch (SVNException e) { - throw new VcsException(e); - } finally { - if (repository != null) { - repository.closeSession(); - } - } - + final String repositoryRoot = getRepositoryRoot(svnLocation); final ChangeBrowserSettings.Filter filter = settings.createFilter(); - getCommittedChangesImpl(settings, svnLocation.getURL(), new String[]{""}, maxCount, new Consumer() { + getCommittedChangesImpl(settings, svnLocation, new String[]{""}, maxCount, new Consumer() { public void consume(final SVNLogEntry svnLogEntry) { final SvnChangeList cl = new SvnChangeList(myVcs, svnLocation, svnLogEntry, repositoryRoot); if (filter.accepts(cl)) { @@ -218,29 +145,9 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi public List getCommittedChanges(ChangeBrowserSettings settings, final RepositoryLocation location, final int maxCount) throws VcsException { final SvnRepositoryLocation svnLocation = (SvnRepositoryLocation) location; final ArrayList result = new ArrayList(); - final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); - if (progress != null) { - progress.setText(SvnBundle.message("progress.text.changes.collecting.changes")); - progress.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", location)); - } + final String repositoryRoot = getRepositoryRoot(svnLocation); - final String repositoryRoot; - // TODO: Implement this with command line - SVNRepository repository = null; - try { - repository = myVcs.createRepository(svnLocation.getURL()); - repositoryRoot = repository.getRepositoryRoot(true).toString(); - repository.closeSession(); - } - catch (SVNException e) { - throw new VcsException(e); - } finally { - if (repository != null) { - repository.closeSession(); - } - } - - getCommittedChangesImpl(settings, svnLocation.getURL(), new String[]{""}, maxCount, new Consumer() { + getCommittedChangesImpl(settings, svnLocation, new String[]{""}, maxCount, new Consumer() { public void consume(final SVNLogEntry svnLogEntry) { result.add(new SvnChangeList(myVcs, svnLocation, svnLogEntry, repositoryRoot)); } @@ -254,14 +161,40 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi final PairConsumer> finalConsumer) throws VcsException { final SvnRepositoryLocation svnLocation = (SvnRepositoryLocation) location; - final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); - if (progress != null) { - progress.setText(SvnBundle.message("progress.text.changes.collecting.changes")); - progress.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", location)); - } - // TODO: Rewrite this without using SVNKit + final String repositoryRoot = getRepositoryRoot(svnLocation); + + final MergeSourceHierarchyBuilder builder = new MergeSourceHierarchyBuilder(new Consumer>() { + public void consume(TreeStructureNode node) { + finalConsumer.consume(new SvnChangeList(myVcs, svnLocation, node.getMe(), repositoryRoot), node); + } + }); + final SvnMergeSourceTracker mergeSourceTracker = new SvnMergeSourceTracker(new ThrowableConsumer, SVNException>() { + public void consume(Pair svnLogEntryIntegerPair) throws SVNException { + builder.consume(svnLogEntryIntegerPair); + } + }); + + getCommittedChangesImpl(settings, svnLocation, new String[]{""}, maxCount, new Consumer() { + public void consume(final SVNLogEntry svnLogEntry) { + try { + mergeSourceTracker.consume(svnLogEntry); + } + catch (SVNException e) { + throw new RuntimeException(e); + // will not occur actually but anyway never eat them + } + } + }, true, false); + + builder.finish(); + } + + private String getRepositoryRoot(@NotNull SvnRepositoryLocation svnLocation) throws VcsException { + // TODO: Implement this with command line + final String repositoryRoot; SVNRepository repository = null; + try { repository = myVcs.createRepository(svnLocation.getURL()); repositoryRoot = repository.getRepositoryRoot(true).toString(); @@ -273,154 +206,124 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi repository.closeSession(); } } - - final MergeTrackerProxy proxy = new MergeTrackerProxy(new Consumer>() { - public void consume(TreeStructureNode node) { - finalConsumer.consume(new SvnChangeList(myVcs, svnLocation, node.getMe(), repositoryRoot), node); - } - }); - final SvnMergeSourceTracker mergeSourceTracker = new SvnMergeSourceTracker(new ThrowableConsumer, SVNException>() { - public void consume(Pair svnLogEntryIntegerPair) throws SVNException { - proxy.consume(svnLogEntryIntegerPair); - } - }); - - getCommittedChangesImpl(settings, svnLocation.getURL(), new String[]{""}, maxCount, new Consumer() { - public void consume(final SVNLogEntry svnLogEntry) { - try { - mergeSourceTracker.consume(svnLogEntry); - } - catch (SVNException e) { - throw new RuntimeException(e); - // will not occur actually but anyway never eat them - } - } - }, true, false); - - proxy.finish(); + return repositoryRoot; } - - private static class MergeTrackerProxy implements ThrowableConsumer, SVNException> { - private TreeStructureNode myCurrentHierarchy; - private final Consumer> myConsumer; - - private MergeTrackerProxy(Consumer> consumer) { - myConsumer = consumer; - } - - public void consume(Pair svnLogEntryIntegerPair) throws SVNException { - final SVNLogEntry logEntry = svnLogEntryIntegerPair.getFirst(); - final Integer mergeLevel = svnLogEntryIntegerPair.getSecond(); - - if (mergeLevel < 0) { - if (myCurrentHierarchy != null) { - myConsumer.consume(myCurrentHierarchy); - } - if (logEntry.hasChildren()) { - myCurrentHierarchy = new TreeStructureNode(logEntry); - } else { - // just pass - myCurrentHierarchy = null; - myConsumer.consume(new TreeStructureNode(logEntry)); - } - } else { - addToLevel(myCurrentHierarchy, logEntry, mergeLevel); - } - } - - public void finish() { - if (myCurrentHierarchy != null) { - myConsumer.consume(myCurrentHierarchy); - } - } - - private static void addToLevel(final TreeStructureNode tree, final SVNLogEntry entry, final int left) { - assert tree != null; - if (left == 0) { - tree.add(entry); - } else { - final List> children = tree.getChildren(); - assert ! children.isEmpty(); - addToLevel(children.get(children.size() - 1), entry, left - 1); - } - } - } - - private void getCommittedChangesImpl(ChangeBrowserSettings settings, final String url, final String[] filterUrls, + private void getCommittedChangesImpl(ChangeBrowserSettings settings, final SvnRepositoryLocation location, final String[] filterUrls, final int maxCount, final Consumer resultConsumer, final boolean includeMergedRevisions, final boolean filterOutByDate) throws VcsException { - final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); - if (progress != null) { - progress.setText(SvnBundle.message("progress.text.changes.collecting.changes")); - progress.setText2(SvnBundle.message("progress.text2.changes.establishing.connection", url)); - } - try { - SVNLogClient logger = myVcs.createLogClient(); + setCollectingChangesProgress(location); + try { final String author = settings.getUserFilter(); final Date dateFrom = settings.getDateAfterFilter(); final Long changeFrom = settings.getChangeAfterFilter(); final Date dateTo = settings.getDateBeforeFilter(); final Long changeTo = settings.getChangeBeforeFilter(); - final SVNRevision revisionBefore; - if (dateTo != null) { - revisionBefore = SVNRevision.create(dateTo); - } - else if (changeTo != null) { - revisionBefore = SVNRevision.create(changeTo.longValue()); - } - else { - // TODO: Implement this with command line - SVNRepository repository = null; - final long revision; - try { - repository = myVcs.createRepository(url); - revision = repository.getLatestRevision(); - } finally { - if (repository != null) { - repository.closeSession(); - } - } - revisionBefore = SVNRevision.create(revision); - } - final SVNRevision revisionAfter; - if (dateFrom != null) { - revisionAfter = SVNRevision.create(dateFrom); - } - else if (changeFrom != null) { - revisionAfter = SVNRevision.create(changeFrom.longValue()); - } - else { - revisionAfter = SVNRevision.create(1); - } + final SVNRevision revisionBefore = createRevisionBefore(location, dateTo, changeTo); + final SVNRevision revisionAfter = createRevisionAfter(dateFrom, changeFrom); // TODO: Implement this with command line - logger.doLog(SVNURL.parseURIEncoded(url), filterUrls, revisionBefore, revisionBefore, revisionAfter, - settings.STOP_ON_COPY, true, includeMergedRevisions, maxCount, null, - new ISVNLogEntryHandler() { - public void handleLogEntry(SVNLogEntry logEntry) { - if (myProject.isDisposed()) throw new ProcessCanceledException(); - if (progress != null) { - progress.setText2(SvnBundle.message("progress.text2.processing.revision", logEntry.getRevision())); - progress.checkCanceled(); - } - if (filterOutByDate && logEntry.getDate() == null) { - // do not add lists without info - this situation is possible for lists where there are paths that user has no rights to observe - return; - } - if (author == null || author.equalsIgnoreCase(logEntry.getAuthor())) { - resultConsumer.consume(logEntry); - } - } - }); + SVNLogClient logger = myVcs.createLogClient(); + logger.doLog(location.toSvnUrl(), filterUrls, revisionBefore, revisionBefore, revisionAfter, settings.STOP_ON_COPY, true, + includeMergedRevisions, maxCount, null, createLogHandler(resultConsumer, filterOutByDate, author)); } catch (SVNException e) { throw new VcsException(e); } } + @NotNull + private static SVNRevision createRevisionAfter(@Nullable Date date, @Nullable Long change) throws VcsException { + return createRevision(date, change, new ThrowableComputable() { + @Override + public SVNRevision compute() throws VcsException { + return SVNRevision.create(1); + } + }); + } + + @NotNull + private SVNRevision createRevisionBefore(@NotNull final SvnRepositoryLocation location, @Nullable Date date, @Nullable Long change) + throws VcsException { + return createRevision(date, change, new ThrowableComputable() { + @Override + public SVNRevision compute() throws VcsException { + return getLatestRevision(location); + } + }); + } + + @NotNull + private SVNRevision getLatestRevision(@NotNull SvnRepositoryLocation location) throws VcsException { + // TODO: Implement this with command line - issue is that if url does not exist, SVNRepository will be created and asked correctly + // TODO: But "svn info -r HEAD" will fail - check if non-existing location could be passed or inteface should be expanded to use + // TODO: peg revisions. + SVNRepository repository = null; + long revision; + + try { + repository = myVcs.createRepository(location.getURL()); + revision = repository.getLatestRevision(); + } + catch (SVNException e) { + throw new SvnBindException(e); + } + finally { + if (repository != null) { + repository.closeSession(); + } + } + + return SVNRevision.create(revision); + } + + @NotNull + private static SVNRevision createRevision(@Nullable Date date, + @Nullable Long change, + @NotNull ThrowableComputable defaultValue) + throws VcsException { + final SVNRevision result; + + if (date != null) { + result = SVNRevision.create(date); + } + else if (change != null) { + result = SVNRevision.create(change.longValue()); + } + else { + result = defaultValue.compute(); + } + + return result; + } + + @NotNull + private ISVNLogEntryHandler createLogHandler(final Consumer resultConsumer, + final boolean filterOutByDate, + final String author) { + return new ISVNLogEntryHandler() { + public void handleLogEntry(SVNLogEntry logEntry) { + if (myProject.isDisposed()) throw new ProcessCanceledException(); + + ProgressManager.progress2(SvnBundle.message("progress.text2.processing.revision", logEntry.getRevision())); + if (filterOutByDate && logEntry.getDate() == null) { + // do not add lists without info - this situation is possible for lists where there are paths that user has no rights to observe + return; + } + if (author == null || author.equalsIgnoreCase(logEntry.getAuthor())) { + resultConsumer.consume(logEntry); + } + } + }; + } + + private static void setCollectingChangesProgress(@Nullable Object location) { + ProgressManager.progress(SvnBundle.message("progress.text.changes.collecting.changes"), + SvnBundle.message("progress.text2.changes.establishing.connection", location)); + } + public ChangeListColumn[] getColumns() { return new ChangeListColumn[] { new ChangeListColumn.ChangeListNumberColumn(SvnBundle.message("revision.title")), @@ -435,39 +338,6 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi myMergeInfoUpdatesListener.addPanel(action); } - private static class ShowHideMergePanel extends ToggleAction { - private final DecoratorManager myManager; - private final ChangeListFilteringStrategy myStrategy; - private boolean myIsSelected; - - public ShowHideMergePanel(final DecoratorManager manager, final ChangeListFilteringStrategy strategy) { - myManager = manager; - myStrategy = strategy; - } - - @Override - public void update(final AnActionEvent e) { - super.update(e); - final Presentation presentation = e.getPresentation(); - presentation.setIcon(SvnIcons.ShowIntegratedFrom); - presentation.setText(SvnBundle.message("committed.changes.action.enable.merge.highlighting")); - presentation.setDescription(SvnBundle.message("committed.changes.action.enable.merge.highlighting.description.text")); - } - - public boolean isSelected(final AnActionEvent e) { - return myIsSelected; - } - - public void setSelected(final AnActionEvent e, final boolean state) { - myIsSelected = state; - if (state) { - myManager.setFilteringStrategy(myStrategy); - } else { - myManager.removeFilteringStrategy(myStrategy.getKey()); - } - } - } - @Nullable public VcsCommittedViewAuxiliary createActions(final DecoratorManager manager, @Nullable final RepositoryLocation location) { final RootsAndBranches rootsAndBranches = new RootsAndBranches(myProject, manager, location); @@ -478,7 +348,7 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi popup.add(rootsAndBranches.getUndoIntegrateAction()); popup.add(new ConfigureBranchesAction()); - final ShowHideMergePanel action = new ShowHideMergePanel(manager, rootsAndBranches.getStrategy()); + final ShowHideMergePanelAction action = new ShowHideMergePanelAction(manager, rootsAndBranches.getStrategy()); return new VcsCommittedViewAuxiliary(Collections.singletonList(popup), new Runnable() { public void run() { @@ -496,73 +366,7 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi @Override public Pair getOneList(final VirtualFile file, VcsRevisionNumber number) throws VcsException { - final RootUrlInfo rootUrlInfo = myVcs.getSvnFileUrlMapping().getWcRootForFilePath(new File(file.getPath())); - if (rootUrlInfo == null) return null; - final VirtualFile root = rootUrlInfo.getVirtualFile(); - if (root == null) return null; - final SvnRepositoryLocation svnRootLocation = (SvnRepositoryLocation)getLocationFor(new FilePathImpl(root)); - if (svnRootLocation == null) return null; - final String url = svnRootLocation.getURL(); - final long revision; - try { - revision = Long.parseLong(number.asString()); - } catch (NumberFormatException e) { - throw new VcsException(e); - } - - final SvnChangeList[] result = new SvnChangeList[1]; - final SVNLogClient logger; - final SVNRevision revisionBefore; - final SVNURL repositoryUrl; - final SVNURL svnurl; - final SVNInfo targetInfo; - try { - logger = myVcs.createLogClient(); - revisionBefore = SVNRevision.create(revision); - - svnurl = SVNURL.parseURIEncoded(url); - final SVNInfo info = myVcs.getInfo(svnurl, SVNRevision.HEAD); - targetInfo = myVcs.getInfo(new File(file.getPath())); - if (info == null) { - throw new VcsException("Can not get repository URL"); - } - repositoryUrl = info.getRepositoryRootURL(); - } - catch (SVNException e) { - LOG.info(e); - throw new VcsException(e); - } - - tryExactHit(svnRootLocation, result, logger, revisionBefore, repositoryUrl, svnurl); - if (result[0] == null) { - tryByRoot(result, logger, revisionBefore, repositoryUrl); - if (result[0] == null) { - FilePath path = tryStepByStep(svnRootLocation, result, logger, revisionBefore, targetInfo, svnurl); - path = path == null ? new FilePathImpl(file) : path; - // and pass & take rename context there - return new Pair(result[0], path); - } - } - if (result[0].getChanges().size() == 1) { - final Collection changes = result[0].getChanges(); - final Change change = changes.iterator().next(); - final ContentRevision afterRevision = change.getAfterRevision(); - if (afterRevision != null) { - return new Pair(result[0], afterRevision.getFile()); - } else { - return new Pair(result[0], new FilePathImpl(file)); - } - } - String relativePath = SVNPathUtil.getRelativePath(targetInfo.getRepositoryRootURL().toString(), targetInfo.getURL().toString()); - relativePath = relativePath.startsWith("/") ? relativePath : "/" + relativePath; - final Change targetChange = result[0].getByPath(relativePath); - if (targetChange == null) { - FilePath path = tryStepByStep(svnRootLocation, result, logger, revisionBefore, targetInfo, svnurl); - path = path == null ? new FilePathImpl(file) : path; - // and pass & take rename context there - return new Pair(result[0], path); - } - return new Pair(result[0], new FilePathImpl(file)); + return new SingleCommittedListProvider(myVcs, file, number).run(); } @Override @@ -576,121 +380,6 @@ public class SvnCommittedChangesProvider implements CachingCommittedChangesProvi return true; } - private static class RenameContext { - @NotNull - private String myCurrentPath; - private String myRepositoryRoot; - private boolean myHadChanged; - - private RenameContext(final SVNInfo info) { - myRepositoryRoot = info.getRepositoryRootURL().toString(); - myCurrentPath = SVNPathUtil.getRelativePath(myRepositoryRoot, info.getURL().toString()); - myCurrentPath = myCurrentPath.startsWith("/") ? myCurrentPath : ("/" + myCurrentPath); - } - - public void accept(final SVNLogEntry entry) { - final Map changedPaths = entry.getChangedPaths(); - if (changedPaths == null) return; - - for (Object o : changedPaths.values()) { - final SVNLogEntryPath entryPath = (SVNLogEntryPath) o; - if (entryPath != null && 'A' == entryPath.getType() && entryPath.getCopyPath() != null) { - if (myCurrentPath.equals(entryPath.getPath())) { - myHadChanged = true; - myCurrentPath = entryPath.getCopyPath(); - return; - } else if (SVNPathUtil.isAncestor(entryPath.getPath(), myCurrentPath)) { - final String relativePath = SVNPathUtil.getRelativePath(entryPath.getPath(), myCurrentPath); - myCurrentPath = SVNPathUtil.append(entryPath.getCopyPath(), relativePath); - myHadChanged = true; - return; - } - } - } - } - - @Nullable - public FilePath getFilePath(final SvnVcs vcs) { - if (! myHadChanged) return null; - final SvnFileUrlMapping svnFileUrlMapping = vcs.getSvnFileUrlMapping(); - final String absolutePath = SVNPathUtil.append(myRepositoryRoot, myCurrentPath); - final String localPath = svnFileUrlMapping.getLocalPath(absolutePath); - if (localPath == null) { - LOG.info("Cannot find local path for url: " + absolutePath); - return null; - } - return new FilePathImpl(new File(localPath), false); - } - } - - private void tryByRoot(SvnChangeList[] result, SVNLogClient logger, SVNRevision revisionBefore, SVNURL repositoryUrl) throws VcsException { - final boolean authorized = SvnAuthenticationNotifier.passiveValidation(myProject, repositoryUrl); - if (! authorized) return; - tryExactHit(new SvnRepositoryLocation(repositoryUrl.toString()), result, logger, revisionBefore, repositoryUrl, repositoryUrl); - } - - // return changed path, if any - private FilePath tryStepByStep(final SvnRepositoryLocation svnRepositoryLocation, - final SvnChangeList[] result, - SVNLogClient logger, - final SVNRevision revisionBefore, final SVNInfo info, SVNURL svnurl) throws VcsException { - final String repositoryRoot = info.getRepositoryRootURL().toString(); - try { - final RenameContext renameContext = new RenameContext(info); - // TODO: Implement this with command line - logger.doLog(svnurl, null, SVNRevision.UNDEFINED, SVNRevision.HEAD, revisionBefore, - false, true, false, 0, null, - new ISVNLogEntryHandler() { - public void handleLogEntry(SVNLogEntry logEntry) { - if (myProject.isDisposed()) throw new ProcessCanceledException(); - if (logEntry.getDate() == null) { - // do not add lists without info - this situation is possible for lists where there are paths that user has no rights to observe - return; - } - renameContext.accept(logEntry); - if (logEntry.getRevision() == revisionBefore.getNumber()) { - result[0] = new SvnChangeList(myVcs, svnRepositoryLocation, logEntry, repositoryRoot); - } - } - }); - return renameContext.getFilePath(myVcs); - } - catch (SVNException e) { - LOG.info(e); - throw new VcsException(e); - } - } - - private void tryExactHit(final SvnRepositoryLocation location, - final SvnChangeList[] result, - SVNLogClient logger, - SVNRevision revisionBefore, - final SVNURL repositoryUrl, SVNURL svnurl) throws VcsException { - try { - // TODO: Implement this with command line - logger.doLog(svnurl, null, SVNRevision.UNDEFINED, revisionBefore, revisionBefore, - false, true, false, 1, null, - new ISVNLogEntryHandler() { - public void handleLogEntry(SVNLogEntry logEntry) { - if (myProject.isDisposed()) throw new ProcessCanceledException(); - if (logEntry.getDate() == null) { - // do not add lists without info - this situation is possible for lists where there are paths that user has no rights to observe - return; - } - result[0] = new SvnChangeList(myVcs, location, logEntry, repositoryUrl.toString()); - } - }); - } - catch (SVNException e) { - LOG.info(e); - if (SVNErrorCode.FS_CATEGORY == e.getErrorMessage().getErrorCode().getCategory()) { - // pass to step by step looking for revision - return; - } - throw new VcsException(e); - } - } - public int getFormatVersion() { return VERSION_WITH_REPLACED_PATHS; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedListsZipper.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedListsZipper.java new file mode 100644 index 000000000000..13359c4d5f23 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCommittedListsZipper.java @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2014 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.diagnostic.Logger; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vcs.RepositoryLocation; +import com.intellij.openapi.vcs.changes.committed.RepositoryLocationGroup; +import com.intellij.openapi.vcs.changes.committed.VcsCommittedListsZipper; +import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.idea.svn.SvnUtil; +import org.jetbrains.idea.svn.SvnVcs; +import org.tmatesoft.svn.core.SVNURL; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** +* @author Konstantin Kolosovsky. +*/ +public class SvnCommittedListsZipper implements VcsCommittedListsZipper { + + private static final Logger LOG = Logger.getInstance(SvnCommittedListsZipper.class); + + @NotNull private final SvnVcs myVcs; + + public SvnCommittedListsZipper(@NotNull SvnVcs vcs) { + myVcs = vcs; + } + + public Pair, List> groupLocations(final List in) { + final List groups = new ArrayList(); + final List singles = new ArrayList(); + + final MultiMap map = new MultiMap(); + + for (RepositoryLocation location : in) { + final SvnRepositoryLocation svnLocation = (SvnRepositoryLocation) location; + final String url = svnLocation.getURL(); + + final SVNURL root = SvnUtil.getRepositoryRoot(myVcs, url); + if (root == null) { + // should not occur + LOG.info("repository root not found for location:"+ location.toPresentableString()); + singles.add(location); + } else { + map.putValue(root, svnLocation); + } + } + + final Set keys = map.keySet(); + for (SVNURL key : keys) { + final Collection repositoryLocations = map.get(key); + if (repositoryLocations.size() == 1) { + singles.add(repositoryLocations.iterator().next()); + } else { + final SvnRepositoryLocationGroup group = new SvnRepositoryLocationGroup(key, repositoryLocations); + groups.add(group); + } + } + return new Pair, List>(groups, singles); + } + + public CommittedChangeList zip(final RepositoryLocationGroup group, final List lists) { + return new SvnChangeList(lists, new SvnRepositoryLocation(group.toPresentableString())); + } + + public long getNumber(final CommittedChangeList list) { + return list.getNumber(); + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCopyPathTracker.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCopyPathTracker.java new file mode 100644 index 000000000000..70e982aeff8e --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnCopyPathTracker.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2014 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.diagnostic.Logger; +import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.FilePathImpl; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.idea.svn.SvnFileUrlMapping; +import org.jetbrains.idea.svn.SvnVcs; +import org.tmatesoft.svn.core.SVNLogEntry; +import org.tmatesoft.svn.core.SVNLogEntryPath; +import org.tmatesoft.svn.core.internal.util.SVNPathUtil; + +import java.io.File; +import java.util.Map; + +/** +* @author Konstantin Kolosovsky. +*/ +public class SvnCopyPathTracker { + + private static final Logger LOG = Logger.getInstance(SvnCopyPathTracker.class); + + @NotNull private String myCurrentPath; + private String myRepositoryRoot; + private boolean myHadChanged; + + public SvnCopyPathTracker(@NotNull String repositoryUrl, @NotNull String relativeUrl) { + myRepositoryRoot = repositoryUrl; + myCurrentPath = relativeUrl; + } + + public void accept(@NotNull final SVNLogEntry entry) { + final Map changedPaths = entry.getChangedPaths(); + if (changedPaths == null) return; + + for (Object o : changedPaths.values()) { + final SVNLogEntryPath entryPath = (SVNLogEntryPath) o; + if (entryPath != null && 'A' == entryPath.getType() && entryPath.getCopyPath() != null) { + if (myCurrentPath.equals(entryPath.getPath())) { + myHadChanged = true; + myCurrentPath = entryPath.getCopyPath(); + return; + } else if (SVNPathUtil.isAncestor(entryPath.getPath(), myCurrentPath)) { + final String relativePath = SVNPathUtil.getRelativePath(entryPath.getPath(), myCurrentPath); + myCurrentPath = SVNPathUtil.append(entryPath.getCopyPath(), relativePath); + myHadChanged = true; + return; + } + } + } + } + + @Nullable + public FilePath getFilePath(final SvnVcs vcs) { + if (! myHadChanged) return null; + final SvnFileUrlMapping svnFileUrlMapping = vcs.getSvnFileUrlMapping(); + final String absolutePath = SVNPathUtil.append(myRepositoryRoot, myCurrentPath); + final String localPath = svnFileUrlMapping.getLocalPath(absolutePath); + if (localPath == null) { + LOG.info("Cannot find local path for url: " + absolutePath); + return null; + } + return new FilePathImpl(new File(localPath), false); + } +} 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 931fcc6a5977..92b619a7465f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java @@ -44,7 +44,6 @@ 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.wc.SVNInfo; -import org.tmatesoft.svn.core.wc.SVNLogClient; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.SvnTarget; import org.tmatesoft.svn.util.SVNLogType; @@ -386,7 +385,10 @@ public class SvnHistoryProvider final SVNURL svnurl = SVNURL.parseURIEncoded(myUrl); SVNRevision operationalFrom = myFrom == null ? SVNRevision.HEAD : myFrom; - final SVNURL rootURL = getRepositoryRoot(svnurl, myFrom); + // TODO: try to rewrite without separately retrieving repository url by item url - as this command could require authentication + // TODO: and it is not "clear enough/easy to implement" with current design (for some cases) how to cache credentials (if in + // TODO: non-interactive mode) + final SVNURL rootURL = SvnUtil.getRepositoryRoot(myVcs, svnurl); if (rootURL == null) { throw new VcsException("Could not find repository root for URL: " + myUrl); } @@ -415,37 +417,30 @@ public class SvnHistoryProvider } private void loadBackwards(SVNURL svnurl) throws SVNException, VcsException { - final SVNURL rootURL = getRepositoryRoot(svnurl, myFrom); - final String root = rootURL.toString(); - String relativeUrl = myUrl; - if (myUrl.startsWith(root)) { - relativeUrl = myUrl.substring(root.length()); - } + // this method is called when svnurl does not exist in latest repository revision - thus concrete old revision is used for "info" + // command to get repository url + SVNInfo info = myVcs.getInfo(svnurl, myPeg, myPeg); + final SVNURL rootURL = info != null ? info.getRepositoryRootURL() : null; + final String root = rootURL != null ? rootURL.toString() : ""; + String relativeUrl = myUrl; + if (myUrl.startsWith(root)) { + relativeUrl = myUrl.substring(root.length()); + } - // TODO: Update this call to myVcs.getFactory.createHistoryClient - SVNLogClient client = myVcs.createLogClient(); - - final RepositoryLogEntryHandler repositoryLogEntryHandler = + final RepositoryLogEntryHandler repositoryLogEntryHandler = new RepositoryLogEntryHandler(myVcs, myUrl, SVNRevision.UNDEFINED, relativeUrl, new ThrowableConsumer() { @Override public void consume(VcsFileRevision revision) throws SVNException { myConsumer.consume(revision); - throw new SVNCancelException(); // load only one revision } }, rootURL); - repositoryLogEntryHandler.setThrowCancelOnMeetPathCreation(true); + repositoryLogEntryHandler.setThrowCancelOnMeetPathCreation(true); - client.doLog(rootURL, new String[]{}, myFrom, myFrom, myTo == null ? SVNRevision.create(1) : myTo, false, true, myShowMergeSources && mySupport15, 0, null, repositoryLogEntryHandler); - } - - // TODO: try to rewrite without separately retrieving repository url by item url - as this command could require authentication - // TODO: and it is not "clear enough/easy to implement" with current design (for some cases) how to cache credentials (if in - // TODO: non-interactive mode) - private SVNURL getRepositoryRoot(SVNURL svnurl, SVNRevision operationalFrom) throws SVNException { - SVNInfo info = myVcs.getInfo(svnurl, SVNRevision.HEAD); - - return info != null ? info.getRepositoryRootURL() : null; + SvnTarget target = SvnTarget.fromURL(rootURL, myFrom); + myVcs.getFactory(target).createHistoryClient() + .doLog(target, myFrom, myTo == null ? SVNRevision.create(1) : myTo, false, true, myShowMergeSources && mySupport15, 1, null, + repositoryLogEntryHandler); } private boolean existsNow(SVNURL svnurl) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java index bf97f21b809a..f58e6c8f542f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnKitHistoryClient.java @@ -5,6 +5,7 @@ import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; +import org.jetbrains.idea.svn.commandLine.SvnBindException; import org.tmatesoft.svn.core.ISVNLogEntryHandler; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.SVNLogClient; @@ -42,7 +43,7 @@ public class SvnKitHistoryClient extends BaseSvnClient implements HistoryClient } } catch (SVNException e) { - throw new VcsException(e); + throw new SvnBindException(e); } } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLoadingRepositoryLocation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLoadingRepositoryLocation.java deleted file mode 100644 index 3fcc5c32c94f..000000000000 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLoadingRepositoryLocation.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2000-2009 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 org.jetbrains.annotations.NotNull; -import org.jetbrains.idea.svn.SvnVcs; - -public class SvnLoadingRepositoryLocation extends SvnRepositoryLocation { - private final LocationDetector locationDetector; - - public SvnLoadingRepositoryLocation(final FilePath rootFile, @NotNull final String URL, @NotNull final SvnVcs vcs) { - super(URL); - locationDetector = new LocationDetector(vcs); - } - - public SvnLoadingRepositoryLocation(@NotNull final String URL, @NotNull final SvnVcs vcs) { - super(URL); - locationDetector = new LocationDetector(vcs); - } - -} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLogLoader.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLogLoader.java index 4ed82c6ed78b..0ff2e3b34e46 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLogLoader.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLogLoader.java @@ -15,13 +15,14 @@ */ package org.jetbrains.idea.svn.history; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; -import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.wc.SVNRevision; import java.util.List; public interface SvnLogLoader { List loadInterval(final SVNRevision fromIncluding, final SVNRevision toIncluding, - final int maxCount, final boolean includingYoungest, final boolean includeOldest) throws SVNException; + final int maxCount, final boolean includingYoungest, final boolean includeOldest) + throws VcsException; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLogUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLogUtil.java index db229555c968..7f4f25f023a9 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLogUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnLogUtil.java @@ -19,15 +19,16 @@ import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; +import org.jetbrains.annotations.NotNull; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.ISVNLogEntryHandler; -import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.wc.SVNLogClient; import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.util.ArrayList; import java.util.List; @@ -37,48 +38,47 @@ public class SvnLogUtil implements SvnLogLoader { private final SvnVcs myVcs; private final SvnRepositoryLocation myLocation; private final SVNURL myRepositoryRoot; - private final String myRelative; public SvnLogUtil(final Project project, final SvnVcs vcs, final SvnRepositoryLocation location, final SVNURL repositoryRoot) { myProject = project; myVcs = vcs; myLocation = location; myRepositoryRoot = repositoryRoot; - - final String repositoryRootPath = repositoryRoot.toString(); - myRelative = myLocation.getURL().substring(repositoryRootPath.length()); } public List loadInterval(final SVNRevision fromIncluding, final SVNRevision toIncluding, - final int maxCount, final boolean includingYoungest, final boolean includeOldest) throws SVNException { + final int maxCount, final boolean includingYoungest, final boolean includeOldest) + throws VcsException { final List result = new ArrayList(); - loadRevisions(fromIncluding, toIncluding, null, maxCount, result, includingYoungest, includeOldest); + ISVNLogEntryHandler handler = createLogHandler(fromIncluding, toIncluding, includingYoungest, includeOldest, result); + SvnTarget target = SvnTarget.fromURL(myLocation.toSvnUrl()); + + myVcs.getFactory(target).createHistoryClient().doLog(target, fromIncluding, toIncluding, true, true, false, maxCount, null, handler); + return result; } - private void loadRevisions(final SVNRevision fromIncluding, final SVNRevision toIncluding, final String author, final int maxCount, - final List result, - final boolean includingYoungest, final boolean includeOldest) throws SVNException { - SVNLogClient logger = myVcs.createLogClient(); - logger.doLog(myRepositoryRoot, new String[]{myRelative}, SVNRevision.UNDEFINED, fromIncluding, toIncluding, true, true, maxCount, - new ISVNLogEntryHandler() { - public void handleLogEntry(SVNLogEntry logEntry) { - if (myProject.isDisposed()) throw new ProcessCanceledException(); - final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); - if (progress != null) { - progress.setText2(SvnBundle.message("progress.text2.processing.revision", logEntry.getRevision())); - progress.checkCanceled(); - } - if ((! includingYoungest) && (logEntry.getRevision() == fromIncluding.getNumber())) { - return; - } - if ((! includeOldest) && (logEntry.getRevision() == toIncluding.getNumber())) { - return; - } - if (author == null || author.equalsIgnoreCase(logEntry.getAuthor())) { - result.add(new SvnChangeList(myVcs, myLocation, logEntry, myRepositoryRoot.toString())); - } - } - }); + @NotNull + private ISVNLogEntryHandler createLogHandler(final SVNRevision fromIncluding, + final SVNRevision toIncluding, + final boolean includingYoungest, + final boolean includeOldest, final List result) { + return new ISVNLogEntryHandler() { + public void handleLogEntry(SVNLogEntry logEntry) { + if (myProject.isDisposed()) throw new ProcessCanceledException(); + final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator(); + if (progress != null) { + progress.setText2(SvnBundle.message("progress.text2.processing.revision", logEntry.getRevision())); + progress.checkCanceled(); + } + if ((!includingYoungest) && (logEntry.getRevision() == fromIncluding.getNumber())) { + return; + } + if ((!includeOldest) && (logEntry.getRevision() == toIncluding.getNumber())) { + return; + } + result.add(new SvnChangeList(myVcs, myLocation, logEntry, myRepositoryRoot.toString())); + } + }; } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryLocation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryLocation.java index 3d5bcaf0d92b..5dc605b8ed57 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryLocation.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRepositoryLocation.java @@ -18,10 +18,15 @@ package org.jetbrains.idea.svn.history; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.RepositoryLocation; import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.actions.VcsContextFactory; import com.intellij.util.NotNullFunction; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.RootUrlInfo; +import org.jetbrains.idea.svn.SvnUtil; import org.jetbrains.idea.svn.SvnVcs; +import org.jetbrains.idea.svn.commandLine.SvnBindException; +import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; import java.io.File; @@ -63,10 +68,24 @@ public class SvnRepositoryLocation implements RepositoryLocation { public static FilePath getLocalPath(final String fullPath, final NotNullFunction detector, final SvnVcs vcs) { if (vcs.getProject().isDefault()) return null; final RootUrlInfo rootForUrl = vcs.getSvnFileUrlMapping().getWcRootForUrl(fullPath); + FilePath result = null; + if (rootForUrl != null) { - return LocationDetector.filePathByUrlAndPath(fullPath, rootForUrl.getUrl().toString(), rootForUrl.getIoFile().getAbsolutePath(), detector); + String relativePath = SvnUtil.getRelativeUrl(rootForUrl.getUrl(), fullPath); + File file = new File(rootForUrl.getPath(), relativePath); + + result = VcsContextFactory.SERVICE.getInstance().createFilePathOn(file, detector); } - return null; + return result; + } + + public SVNURL toSvnUrl() throws VcsException { + try { + return SvnUtil.createUrl(myURL); + } + catch (SVNException e) { + throw new SvnBindException(e); + } } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRevisionsNavigationMediator.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRevisionsNavigationMediator.java index 26e5152b2d7f..8f29e01a8403 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRevisionsNavigationMediator.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnRevisionsNavigationMediator.java @@ -29,8 +29,8 @@ import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.idea.svn.SvnVcs; import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.io.SVNRepository; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; import java.util.*; @@ -55,31 +55,30 @@ public class SvnRevisionsNavigationMediator implements CommittedChangesNavigatio myChunks = new LinkedList>(); - final SVNURL[] repositoryRoot = new SVNURL[1]; - final long[] youngRevision = new long[1]; final VcsException[] exception = new VcsException[1]; + final Ref infoRef = new Ref(); Runnable process = new Runnable() { @Override public void run() { - SVNRepository repository = null; try { - repository = vcs.createRepository(location.getURL()); - youngRevision[0] = repository.getLatestRevision(); - repositoryRoot[0] = repository.getRepositoryRoot(false); + infoRef.set(vcs.getInfo(location.toSvnUrl(), SVNRevision.HEAD)); + } + catch (VcsException e) { + exception[0] = e; } catch (SVNException e) { exception[0] = new VcsException(e); } - finally { - if (repository != null) { - repository.closeSession(); - } - } } }; underProgress(exception, process); + SVNInfo info = infoRef.get(); + if (info == null || info.getRevision() == null || info.getRepositoryRootURL() == null) { + throw new VcsException("Could not get head info for " + location); + } + final Iterator visualIterator = project.isDefault() ? null : CommittedChangesCache.getInstance(project).getBackBunchedIterator(vcs, vcsRoot, location, CHUNK_SIZE); final Iterator internalIterator = project.isDefault() ? null : LoadedRevisionsCache.getInstance(project).iterator(location.getURL()); @@ -87,8 +86,9 @@ public class SvnRevisionsNavigationMediator implements CommittedChangesNavigatio myInternallyCached = (internalIterator == null) ? null : new InternallyCachedProvider(internalIterator, myProject); myVisuallyCached = (visualIterator == null) ? null : new VisuallyCachedProvider(visualIterator, myProject, location); - myChunkFactory = new BunchFactory(myInternallyCached, myVisuallyCached, - new LiveProvider(vcs, location, youngRevision[0], new SvnLogUtil(myProject, vcs, location, repositoryRoot[0]))); + myChunkFactory = new BunchFactory(myInternallyCached, myVisuallyCached, new LiveProvider(vcs, location, info.getRevision().getNumber(), + new SvnLogUtil(myProject, vcs, location, + info.getRepositoryRootURL()))); myCurrentIdx = -1; @@ -133,13 +133,7 @@ public class SvnRevisionsNavigationMediator implements CommittedChangesNavigatio } final Ref canNotGoBackRef = new Ref(); - final List fragments; - try { - fragments = myChunkFactory.goBack(CHUNK_SIZE, canNotGoBackRef); - } - catch (SVNException e) { - throw new VcsException(e); - } + final List fragments = myChunkFactory.goBack(CHUNK_SIZE, canNotGoBackRef); myCanNotGoBack = canNotGoBackRef.get().booleanValue(); if (! fragments.isEmpty()) { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java index 29ff70e59a5f..ce6d2b219bca 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/CmdPropertyClient.java @@ -8,14 +8,8 @@ import org.jetbrains.idea.svn.api.BaseSvnClient; import org.jetbrains.idea.svn.commandLine.CommandExecutor; import org.jetbrains.idea.svn.commandLine.CommandUtil; import org.jetbrains.idea.svn.commandLine.SvnCommandName; -import org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNPropertyValue; -import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; -import org.tmatesoft.svn.core.wc.SVNInfo; -import org.tmatesoft.svn.core.wc.SVNPropertyData; -import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnTarget; import javax.xml.bind.JAXBException; @@ -98,6 +92,39 @@ public class CmdPropertyClient extends BaseSvnClient implements PropertyClient { runSetProperty(SvnTarget.fromFile(file), property, null, depth, value, force); } + @Override + public void setProperties(@NotNull File file, @NotNull SVNProperties properties) throws VcsException { + SVNProperties currentProperties = collectPropertiesToDelete(file); + currentProperties.putAll(properties); + + for (String propertyName : currentProperties.nameSet()) { + setProperty(file, propertyName, currentProperties.getSVNPropertyValue(propertyName), SVNDepth.EMPTY, true); + } + } + + @NotNull + private SVNProperties collectPropertiesToDelete(@NotNull File file) throws VcsException { + final SVNProperties result = new SVNProperties(); + + list(SvnTarget.fromFile(file), null, SVNDepth.EMPTY, new ISVNPropertyHandler() { + @Override + public void handleProperty(File path, SVNPropertyData property) throws SVNException { + // null indicates property will be deleted + result.put(property.getName(), (SVNPropertyValue)null); + } + + @Override + public void handleProperty(SVNURL url, SVNPropertyData property) throws SVNException { + } + + @Override + public void handleProperty(long revision, SVNPropertyData property) throws SVNException { + } + }); + + return result; + } + @Override public void setRevisionProperty(@NotNull SvnTarget target, @NotNull String property, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java index 12bc0e9c885c..b44f7ea12e75 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/PropertyClient.java @@ -7,6 +7,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.SvnClient; import org.tmatesoft.svn.core.SVNDepth; +import org.tmatesoft.svn.core.SVNProperties; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions; import org.tmatesoft.svn.core.wc.ISVNOptions; @@ -51,6 +52,8 @@ public interface PropertyClient extends SvnClient { @Nullable SVNDepth depth, boolean force) throws VcsException; + void setProperties(@NotNull File file, @NotNull SVNProperties properties) throws VcsException; + void setRevisionProperty(@NotNull SvnTarget target, @NotNull String property, @NotNull SVNRevision revision, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java index d55eb686371c..c745c4246b67 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/properties/SvnKitPropertyClient.java @@ -5,14 +5,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.api.BaseSvnClient; import org.jetbrains.idea.svn.commandLine.SvnBindException; -import org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNPropertyValue; -import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.wc.ISVNPropertyHandler; -import org.tmatesoft.svn.core.wc.SVNPropertyData; -import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNWCClient; +import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.wc.*; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; @@ -83,6 +77,21 @@ public class SvnKitPropertyClient extends BaseSvnClient implements PropertyClien } } + @Override + public void setProperties(@NotNull File file, @NotNull SVNProperties properties) throws VcsException { + try { + createClient().doSetProperty(file, new ISVNPropertyValueProvider() { + @Override + public SVNProperties providePropertyValues(File path, SVNProperties properties) throws SVNException { + return properties; + } + }, true, SVNDepth.EMPTY, null, null); + } + catch (SVNException e) { + throw new SvnBindException(e); + } + } + @Override public void setRevisionProperty(@NotNull SvnTarget target, @NotNull String property, diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java index 87f0e23a3fc5..02992c9c591f 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/revert/CmdRevertClient.java @@ -39,7 +39,8 @@ public class CmdRevertClient extends BaseSvnClient implements RevertClient { // TODO: handler should be called in parallel with command execution, but this will be in other thread // TODO: check if that is ok for current handler implementation // TODO: add possibility to invoke "handler.checkCancelled" - process should be killed - CommandExecutor command = CommandUtil.execute(myVcs, SvnTarget.fromFile(paths[0]), SvnCommandName.revert, parameters, null); + CommandExecutor command = + CommandUtil.execute(myVcs, SvnTarget.fromFile(paths[0]), CommandUtil.getHomeDirectory(), SvnCommandName.revert, parameters, null); FileStatusResultParser parser = new FileStatusResultParser(CHANGED_PATH, handler, new RevertStatusConvertor()); parser.parse(command.getOutput()); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java index ed097b42a85a..38f15dd271f8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/rollback/SvnRollbackEnvironment.java @@ -24,13 +24,13 @@ import com.intellij.openapi.vcs.rollback.RollbackProgressListener; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.Processor; -import com.intellij.util.containers.MultiMap; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.*; -import org.jetbrains.idea.svn.revert.RevertClient; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.wc.*; +import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; import java.io.IOException; @@ -94,12 +94,12 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { }; final List fromToModified = new ArrayList(); - final MultiMap properties = new MultiMap(); + final Map properties = ContainerUtil.newHashMap(); moveRenamesToTmp(exceptions, fromToModified, properties, collector); // adds (deletes) // deletes (adds) // modifications - final Reverter reverter = new Reverter(mySvnVcs.getFactory().createRevertClient(), revertHandler, exceptions); + final Reverter reverter = new Reverter(mySvnVcs, revertHandler, exceptions); reverter.revert(checker.getForAdds(), true); reverter.revert(checker.getForDeletes(), true); final List edits = checker.getForEdits(); @@ -117,18 +117,20 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { private void moveRenamesToTmp(List exceptions, List fromToModified, - final MultiMap properties, + final Map properties, final UnversionedAndNotTouchedFilesGroupCollector collector) { final Map fromTo = collector.getFromTo(); try { final File tmp = FileUtil.createTempDirectory("forRename", ""); - final SVNWCClient client = mySvnVcs.createWCClient(); final ISVNPropertyHandler handler = new ISVNPropertyHandler() { @Override public void handleProperty(File path, SVNPropertyData property) throws SVNException { final ThroughRenameInfo info = collector.findToFile(new FilePathImpl(path, path.isDirectory()), null); if (info != null) { - properties.putValue(info.getTo(), property); + if (!properties.containsKey(info.getTo())) { + properties.put(info.getTo(), new SVNProperties()); + } + properties.get(info.getTo()).put(property.getName(), property.getValue()); } } @@ -148,7 +150,7 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { final File source = entry.getKey(); final ThroughRenameInfo info = entry.getValue(); if (info.isVersioned()) { - client.doGetProperty(source, null, SVNRevision.UNDEFINED, SVNRevision.WORKING, SVNDepth.EMPTY, handler, null); + mySvnVcs.getFactory(source).createPropertyClient().list(SvnTarget.fromFile(source), SVNRevision.WORKING, SVNDepth.EMPTY, handler); } if (source.isDirectory()) { if (! FileUtil.filesEqual(info.getTo(), info.getFirstTo())) { @@ -166,14 +168,14 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { catch (IOException e) { exceptions.add(new VcsException(e)); } - catch (SVNException e) { - exceptions.add(new VcsException(e)); + catch(VcsException e) { + exceptions.add(e); } } private void moveGroup(final List exceptions, List fromTo, - MultiMap properties) { + Map properties) { Collections.sort(fromTo, new Comparator() { @Override public int compare(CopiedAsideInfo o1, CopiedAsideInfo o2) { @@ -232,36 +234,25 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { applyProperties(properties, exceptions); } - private void applyProperties(MultiMap propertiesMap, final List exceptions) { - final SVNWCClient client = mySvnVcs.createWCClient(); - for (Map.Entry> entry : propertiesMap.entrySet()) { - final File file = entry.getKey(); - final Collection propertyDatas = entry.getValue(); - try { - client.doSetProperty(file, new ISVNPropertyValueProvider() { - @Override - public SVNProperties providePropertyValues(File path, SVNProperties properties) throws SVNException { - final SVNProperties result = new SVNProperties(); - for (SVNPropertyData data : propertyDatas) { - result.put(data.getName(), data.getValue()); - } - return result; - } - }, true, SVNDepth.EMPTY, null, null); - } - catch (SVNException e) { - exceptions.add(new VcsException(e)); - } + private void applyProperties(Map propertiesMap, final List exceptions) { + for (Map.Entry entry : propertiesMap.entrySet()) { + File file = entry.getKey(); + try { + mySvnVcs.getFactory(file).createPropertyClient().setProperties(file, entry.getValue()); + } + catch (VcsException e) { + exceptions.add(e); + } } } private static class Reverter { - private final RevertClient myClient; + @NotNull private final SvnVcs myVcs; private ISVNEventHandler myHandler; private final List myExceptions; - private Reverter(RevertClient client, ISVNEventHandler handler, List exceptions) { - myClient = client; + private Reverter(@NotNull SvnVcs vcs, ISVNEventHandler handler, List exceptions) { + myVcs = vcs; myHandler = handler; myExceptions = exceptions; } @@ -269,7 +260,8 @@ public class SvnRollbackEnvironment extends DefaultRollbackEnvironment { public void revert(final File[] files, final boolean recursive) { if (files.length == 0) return; try { - myClient.revert(files, recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY, myHandler); + // Files passed here are split into groups by root and working copy format - thus we could determine factory based on first file + myVcs.getFactory(files[0]).createRevertClient().revert(files, recursive ? SVNDepth.INFINITY : SVNDepth.EMPTY, myHandler); } catch (VcsException e) { processRevertError(e); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java index 57a7e219263b..da0b1e7f590b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/TreeConflictRefreshablePanel.java @@ -193,12 +193,6 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel { return EmptyConflictSide.getInstance(); } } - // todo temporally - /*if (SVNNodeKind.DIR.equals(version.getKind())) { - return new HistoryAsBrowseChangesConflictSide(myVcs.getProject(), version); - } else { - return new HistoryConflictSide(myVcs, version); - }*/ HistoryConflictSide side = new HistoryConflictSide(myVcs, version, untilThisOther); if (untilThisOther != null && ! isLeft) { side.setListToReportLoaded(myRightRevisionsList); @@ -314,11 +308,7 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel { additionalPath = myChange.getAfterRevision().getFile(); } } else { - if (myChange.getBeforeRevision() != null) { - mainPath = myChange.getBeforeRevision().getFile(); - } else { - mainPath = myChange.getAfterRevision().getFile(); - } + mainPath = myChange.getBeforeRevision() != null ? myChange.getBeforeRevision().getFile() : myChange.getAfterRevision().getFile(); } return new Paths(mainPath, additionalPath); } @@ -368,15 +358,6 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel { return null; } // my edit, theirs move or delete - /*if (SVNConflictAction.DELETE.equals(description.getConflictAction()) && description.getSourceLeftVersion() != null) { - //todo - return new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - mergeMyEditTheirsDelete(); - } - }; - } */ if (SVNConflictAction.EDIT.equals(description.getConflictAction()) && description.getSourceLeftVersion() != null && SVNConflictReason.DELETED.equals(description.getConflictReason()) && (myChange.isMoved() || myChange.isRenamed()) && myCommittedRevision != null) { @@ -397,10 +378,7 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel { } public static String filePath(FilePath newFilePath) { - return newFilePath.getName() + - " (" + - newFilePath.getParentPath().getPath() + - ")"; + return newFilePath.getName() + " (" + newFilePath.getParentPath().getPath() + ")"; } private static ActionListener createBoth(SVNTreeConflictDescription description) { @@ -524,12 +502,7 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel { @Override public void load() throws SVNException, VcsException { SVNRevision from = SVNRevision.create(myVersion.getPegRevision()); - if (myPeg == null) { - // just a portion of history - myProvider.reportAppendableHistory(myPath, mySessionAdapter, from, myPeg, LIMIT, myPeg, true); - } else { - myProvider.reportAppendableHistory(myPath, mySessionAdapter, from, myPeg, 0, myPeg, true); - } + myProvider.reportAppendableHistory(myPath, mySessionAdapter, from, myPeg, myPeg == null ? LIMIT : 0, myPeg, true); VcsAbstractHistorySession session = mySessionAdapter.getSession(); if (myListToReportLoaded != null && session != null) { List list = session.getRevisionList(); @@ -576,15 +549,4 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel { 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/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java index 0bb436a428ee..d0ad292ece35 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/update/SvnUpdateEnvironment.java @@ -96,13 +96,7 @@ public class SvnUpdateEnvironment extends AbstractSvnUpdateIntegrateEnvironment } private UpdateClient createUpdateClient(SvnConfiguration configuration, File root, boolean isSwitch, SVNURL sourceUrl) { - boolean is17 = WorkingCopyFormat.ONE_DOT_SEVEN.equals(myVcs.getWorkingCopyFormat(root)); - boolean isSupportedProtocol = - SvnAuthenticationManager.HTTP.equals(sourceUrl.getProtocol()) || SvnAuthenticationManager.HTTPS.equals(sourceUrl.getProtocol()); - - // TODO: Update this with just myVcs.getFactory(root) when switch and authentication protocols are implemented for command line - ClientFactory factory = is17 && (isSwitch || !isSupportedProtocol) ? myVcs.getSvnKitFactory() : myVcs.getFactory(root); - final UpdateClient updateClient = factory.createUpdateClient(); + final UpdateClient updateClient = myVcs.getFactory(root).createUpdateClient(); if (! isSwitch) { updateClient.setIgnoreExternals(configuration.isIgnoreExternals()); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCachingRevisionsTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCachingRevisionsTest.java index ef809649cca6..1f0f7f868780 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCachingRevisionsTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn/SvnCachingRevisionsTest.java @@ -3,11 +3,11 @@ package org.jetbrains.idea.svn; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.committed.ChangesBunch; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase; import org.jetbrains.idea.svn.history.*; -import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.wc.SVNRevision; @@ -48,7 +48,8 @@ public class SvnCachingRevisionsTest extends CodeInsightFixtureTestCase { @Override public List loadInterval(final SVNRevision fromIncluding, final SVNRevision toIncluding, final int maxCount, - final boolean includingYoungest, final boolean includeOldest) throws SVNException { + final boolean includingYoungest, final boolean includeOldest) + throws VcsException { long young = fromIncluding.getNumber(); young = (young == -1) ? myRevisions.get(myRevisions.size() - 1) : young; final long old = toIncluding.getNumber(); diff --git a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnCachingRevisionsTest.java b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnCachingRevisionsTest.java index b06502866c81..dca29f03646f 100644 --- a/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnCachingRevisionsTest.java +++ b/plugins/svn4idea/testSource/org/jetbrains/idea/svn16/SvnCachingRevisionsTest.java @@ -3,12 +3,12 @@ package org.jetbrains.idea.svn16; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.committed.ChangesBunch; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.testFramework.fixtures.CodeInsightFixtureTestCase; import org.jetbrains.idea.svn.SvnApplicationSettings; import org.jetbrains.idea.svn.history.*; -import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNLogEntry; import org.tmatesoft.svn.core.wc.SVNRevision; @@ -49,7 +49,8 @@ public class SvnCachingRevisionsTest extends CodeInsightFixtureTestCase { @Override public List loadInterval(final SVNRevision fromIncluding, final SVNRevision toIncluding, final int maxCount, - final boolean includingYoungest, final boolean includeOldest) throws SVNException { + final boolean includingYoungest, final boolean includeOldest) + throws VcsException { long young = fromIncluding.getNumber(); young = (young == -1) ? myRevisions.get(myRevisions.size() - 1) : young; final long old = toIncluding.getNumber();