blob: a075251e7675859661c685fd49274ca80e1752f3 [file] [log] [blame]
/*
* 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.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.SvnVcs;
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.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.util.Map;
// TODO: This one seem to determine revision in which branch was created - copied from trunk.
// TODO: This could be done in one command "svn log <folder> -r 0:HEAD --stop-on-copy --limit 1".
// TODO: Check for 1.7 and rewrite using this approach.
public class FirstInBranch implements Runnable {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.history.FirstInBranch");
@NotNull private final SvnVcs myVcs;
@NotNull private final String myBranchUrl;
@NotNull private final String myTrunkUrl;
@NotNull private final String myRepositoryRoot;
@NotNull private final TransparentlyFailedValueI<CopyData, VcsException> myConsumer;
public FirstInBranch(@NotNull SvnVcs vcs, @NotNull String repositoryRoot, @NotNull String branchUrl, @NotNull String trunkUrl,
@NotNull TransparentlyFailedValueI<CopyData, VcsException> consumer) {
myVcs = vcs;
myRepositoryRoot = repositoryRoot;
myConsumer = consumer;
myBranchUrl = relativePath(repositoryRoot, branchUrl);
myTrunkUrl = relativePath(repositoryRoot, trunkUrl);
}
private static String relativePath(final String parent, final String child) {
String path = SVNPathUtil.getRelativePath(parent, child);
return SvnUtil.ensureStartSlash(path);
}
private SVNURL getRepositoryRootUrl() {
SVNURL result = null;
try {
result = SvnUtil.createUrl(myRepositoryRoot);
}
catch (SvnBindException e) {
LOG.info(e);
myConsumer.fail(e);
}
return result;
}
public void run() {
SVNURL repositoryRootUrl = getRepositoryRootUrl();
if (repositoryRootUrl != null) {
run(repositoryRootUrl);
}
}
private void run(@NotNull SVNURL url) {
try {
run(url, new Consumer<CopyData>() {
@Override
public void consume(CopyData data) {
if (data != null) {
myConsumer.set(data);
}
}
});
}
catch (VcsException e) {
LOG.info(e);
myConsumer.fail(e);
}
}
private void run(@NotNull SVNURL branchURL, @NotNull Consumer<CopyData> copyDataConsumer) throws VcsException {
final SvnTarget target = SvnTarget.fromURL(branchURL);
HistoryClient client = ApplicationManager.getApplication().runReadAction(new Computable<HistoryClient>() {
@Override
public HistoryClient compute() {
if (myVcs.getProject().isDisposed()) return null;
return myVcs.getFactory(target).createHistoryClient();
}
});
if (client == null) return;
try {
client.doLog(target, SVNRevision.HEAD, SVNRevision.create(0), false, true, false, -1, null,
new MyLogEntryHandler(copyDataConsumer, myTrunkUrl, myBranchUrl));
}
catch (SvnBindException e) {
// do not throw cancel exception as this means corresponding copy point is found (if progress indicator was not explicitly cancelled)
if (!e.contains(SVNErrorCode.CANCELLED)) {
throw e;
}
}
}
private static class MyLogEntryHandler implements LogEntryConsumer {
@NotNull private final SvnPathThroughHistoryCorrection myTrunkCorrector;
@NotNull private final SvnPathThroughHistoryCorrection myBranchCorrector;
@NotNull private final Consumer<CopyData> myCopyDataConsumer;
public MyLogEntryHandler(@NotNull Consumer<CopyData> copyDataConsumer, @NotNull String trunkUrl, @NotNull String branchUrl) {
myCopyDataConsumer = copyDataConsumer;
myTrunkCorrector = new SvnPathThroughHistoryCorrection(trunkUrl);
myBranchCorrector = new SvnPathThroughHistoryCorrection(branchUrl);
}
@Override
public void consume(LogEntry logEntry) throws SVNException {
final Map map = logEntry.getChangedPaths();
checkEntries(logEntry, map);
myTrunkCorrector.consume(logEntry);
myBranchCorrector.consume(logEntry);
checkEntries(logEntry, map);
}
private void checkEntries(LogEntry logEntry, Map map) throws SVNCancelException {
for (Object o : map.values()) {
final LogEntryPath path = (LogEntryPath) o;
final String localPath = path.getPath();
final String copyPath = path.getCopyPath();
if ('A' == path.getType()) {
if (checkForCopyCase(logEntry, path, localPath, copyPath, myTrunkCorrector.getCurrentPath(), myBranchCorrector.getCurrentPath())) {
throw new SVNCancelException();
}
}
}
}
private boolean checkForCopyCase(LogEntry logEntry, LogEntryPath path, String localPath, String copyPath,
final String trunkUrl, final String branchUrl) {
if (equalOrParent(localPath, branchUrl) && equalOrParent(copyPath, trunkUrl)) {
myCopyDataConsumer.consume(new CopyData(path.getCopyRevision(), logEntry.getRevision(), true));
return true;
} else {
if ((equalOrParent(copyPath, branchUrl)) && equalOrParent(localPath, trunkUrl)) {
myCopyDataConsumer.consume(new CopyData(path.getCopyRevision(), logEntry.getRevision(), false));
return true;
}
}
return false;
}
private static boolean equalOrParent(String localPath, final String targetPath) {
return targetPath.equals(localPath) || SVNPathUtil.isAncestor(localPath, targetPath);
}
}
}