IDEA-75800 SVN "Merge from..." not working

This commit is contained in:
irengrig
2012-09-03 18:08:15 +04:00
parent e8af1e7c8f
commit 8e07bfc535
20 changed files with 535 additions and 243 deletions
@@ -27,6 +27,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.LineTokenizer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
@@ -144,7 +145,7 @@ public class PatchReader {
myPatches = myPatchContentParser.getResult();
}
public TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> getAdditionalInfo(final Set<String> filterByPaths) {
public TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> getAdditionalInfo(final Set<String> filterByPaths) {
final TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException>
value = new TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException>();
@@ -53,7 +53,7 @@ public class ConcurrentTasks<T> {
final List<Future<?>> futures = new LinkedList<Future<?>>();
for (final Consumer<Consumer<T>> task : myTasks) {
if (myResultKnown) {
myCntAlive.decrementAndGet();
final int decremented = myCntAlive.decrementAndGet();
continue;
}
final Runnable computableProxy = new Runnable() {
@@ -0,0 +1,100 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vcs.changes;
import com.intellij.openapi.util.ThrowableComputable;
import java.util.concurrent.atomic.AtomicReference;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 8/30/12
* Time: 8:19 PM
*/
public class ThreadSafeTransparentlyFailedValue<T,E extends Exception> implements TransparentlyFailedValueI<T,E> {
private final AtomicReference<ThrowableComputable<T, E>> myRef;
public ThreadSafeTransparentlyFailedValue() {
myRef = new AtomicReference<ThrowableComputable<T, E>>();
}
@Override
public void set(T t) {
if (t != null) {
myRef.set(new Value<T, E>(t));
}
}
@Override
public void fail(E e) {
myRef.set(new ExceptionHolder<T, E>(e));
}
@Override
public void failRuntime(RuntimeException e) {
myRef.set(new RuntimeExceptionHolder<T, E>(e));
}
@Override
public T get() throws E {
return myRef.get() == null ? null : myRef.get().compute();
}
@Override
public boolean haveSomething() { // todo correct here
return myRef.get() != null;
}
private static class Value<T,E extends Exception> implements ThrowableComputable<T,E> {
private final T myT;
private Value(final T t) {
myT = t;
}
@Override
public T compute() throws E {
return myT;
}
}
private static class ExceptionHolder<T,E extends Exception> implements ThrowableComputable<T,E> {
private final E myE;
private ExceptionHolder(E e) {
myE = e;
}
@Override
public T compute() throws E {
throw myE;
}
}
private static class RuntimeExceptionHolder<T,E extends Exception> implements ThrowableComputable<T,E> {
private final RuntimeException myException;
private RuntimeExceptionHolder(RuntimeException exception) {
myException = exception;
}
@Override
public T compute() throws E {
throw myException;
}
}
}
@@ -20,25 +20,41 @@ package com.intellij.openapi.vcs.changes;
* Date: 7/5/11
* Time: 3:35 PM
*/
public class TransparentlyFailedValue<T, E extends Exception> {
public class TransparentlyFailedValue<T, E extends Exception> implements TransparentlyFailedValueI<T,E> {
private T t;
private E e;
private RuntimeException myRuntime;
@Override
public void set(final T t) {
this.t = t;
}
@Override
public void fail(final E e) {
this.e = e;
}
@Override
public void failRuntime(final RuntimeException e) {
myRuntime = e;
}
@Override
public T get() throws E {
if (this.e != null) throw this.e;
if (myRuntime != null) throw myRuntime;
return this.t;
}
public void take(final TransparentlyFailedValue<T,E> value) {
this.t = value.t;
this.e = value.e;
myRuntime = value.myRuntime;
}
@Override
public boolean haveSomething() {
return e != null || myRuntime != null || t != null;
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vcs.changes;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 8/30/12
* Time: 8:18 PM
*/
public interface TransparentlyFailedValueI<T, E extends Exception> {
void set(T t);
void fail(E e);
T get() throws E;
void failRuntime(RuntimeException e);
boolean haveSomething();
}
@@ -26,7 +26,7 @@ import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vcs.changes.CommitContext;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Consumer;
@@ -61,7 +61,7 @@ public class ApplyPatchDefaultExecutor implements ApplyPatchExecutor {
public void apply(MultiMap<VirtualFile, FilePatchInProgress> patchGroups,
LocalChangeList localList,
String fileName,
TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
final Collection<PatchApplier> appliers = new LinkedList<PatchApplier>();
final CommitContext commitContext = new CommitContext();
applyAdditionalInfoBefore(myProject, additionalInfo, commitContext);
@@ -82,7 +82,7 @@ public class ApplyPatchDefaultExecutor implements ApplyPatchExecutor {
}
public static void applyAdditionalInfoBefore(final Project project,
TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo,
TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo,
CommitContext commitContext) {
applyAdditionalInfoImpl(project, additionalInfo, commitContext, new Consumer<InfoGroup>() {
@Override
@@ -93,7 +93,7 @@ public class ApplyPatchDefaultExecutor implements ApplyPatchExecutor {
}
private static void applyAdditionalInfo(final Project project,
TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo,
TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo,
CommitContext commitContext) {
applyAdditionalInfoImpl(project, additionalInfo, commitContext, new Consumer<InfoGroup>() {
@Override
@@ -104,7 +104,7 @@ public class ApplyPatchDefaultExecutor implements ApplyPatchExecutor {
}
public static void applyAdditionalInfoImpl(final Project project,
TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo,
TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo,
CommitContext commitContext, final Consumer<InfoGroup> worker) {
final PatchEP[] extensions = Extensions.getExtensions(PatchEP.EP_NAME, project);
if (extensions == null && extensions.length == 0) return;
@@ -17,7 +17,7 @@ package com.intellij.openapi.vcs.changes.patch;
import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.MultiMap;
@@ -33,5 +33,5 @@ public interface ApplyPatchExecutor {
void apply(final MultiMap<VirtualFile, FilePatchInProgress> patchGroups,
final LocalChangeList localList,
String fileName,
TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo);
TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo);
}
@@ -28,7 +28,7 @@ import com.intellij.openapi.vcs.ObjectsConvertor;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.CommitContext;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager;
import com.intellij.openapi.vcs.changes.shelf.ShelvedChangeList;
import com.intellij.openapi.vcs.changes.shelf.ShelvedChangesViewManager;
@@ -65,7 +65,7 @@ public class ImportToShelfExecutor implements ApplyPatchExecutor {
public void apply(final MultiMap<VirtualFile, FilePatchInProgress> patchGroups,
LocalChangeList localList,
final String fileName,
final TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
final TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
final VcsCatchingRunnable vcsCatchingRunnable = new VcsCatchingRunnable() {
@Override
public void runImpl() throws VcsException {
@@ -669,7 +669,7 @@ public class ShelveChangesManager implements ProjectComponent, JDOMExternalizabl
char[] text = FileUtil.loadFileText(new File(patchPath), CharsetToolkit.UTF8);
PatchReader reader = new PatchReader(new CharArrayCharSequence(text));
final List<TextFilePatch> textFilePatches = reader.readAllPatches();
final TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo = reader.getAdditionalInfo(
final TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo = reader.getAdditionalInfo(
null);
ApplyPatchDefaultExecutor.applyAdditionalInfoBefore(project, additionalInfo, commitContext);
return textFilePatches;
@@ -38,6 +38,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew;
import org.jetbrains.idea.svn.dialogs.LockDialog;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
@@ -609,12 +610,22 @@ public class SvnUtil {
}
}
public static String appendMultiParts(@NotNull final String base, @NotNull final String subPath) throws SVNException {
if (StringUtil.isEmpty(subPath)) return base;
final List<String> parts = StringUtil.split(subPath.replace('\\', '/'), "/", true);
String result = base;
for (String part : parts) {
result = SVNPathUtil.append(result, part);
}
return result;
}
public static SVNURL appendMultiParts(@NotNull final SVNURL base, @NotNull final String subPath) throws SVNException {
if (StringUtil.isEmpty(subPath)) return base;
final List<String> parts = StringUtil.split(subPath.replace('\\', '/'), "/", true);
SVNURL result = base;
for (String part : parts) {
result = result.appendPath(part, true);
result = result.appendPath(part, false);
}
return result;
}
@@ -17,19 +17,20 @@ package org.jetbrains.idea.svn.dialogs;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.util.Consumer;
import com.intellij.util.ThrowableConvertor;
import com.intellij.util.ValueHolder;
import com.intellij.util.containers.Convertor;
import com.intellij.util.continuation.TaskDescriptor;
// cache. persistent. by request
public class FactsCalculator<In, Out> {
public class FactsCalculator<In, Out, E extends Exception> {
private final Project myProject;
private final String myTaskTitle;
private final ValueHolder<Out, In> myCache;
private final Convertor<In, Out> myLive;
private final ThrowableConvertor<In, Out, E> myLive;
public FactsCalculator(Project project, String taskTitle, ValueHolder<Out, In> cache, Convertor<In, Out> live) {
public FactsCalculator(Project project, String taskTitle, ValueHolder<Out, In> cache, ThrowableConvertor<In, Out, E> live) {
myProject = project;
myTaskTitle = taskTitle;
myCache = cache;
@@ -37,33 +38,35 @@ public class FactsCalculator<In, Out> {
}
@CalledInAwt
public void get(final In in, final Consumer<Out> resultConsumer) {
createRunOrContinuation(in, resultConsumer).execute();
public void get(final In in, final Consumer<TransparentlyFailedValueI<Out, E>> resultConsumer, final Class<E> clazzE) {
createRunOrContinuation(in, resultConsumer, clazzE).execute();
}
private RunOrContinuation<Out> createRunOrContinuation(final In in, final Consumer<Out> resultConsumer) {
return new RunOrContinuation<Out>(myProject, myTaskTitle) {
private RunOrContinuation<Out,E> createRunOrContinuation(final In in, final Consumer<TransparentlyFailedValueI<Out, E>> resultConsumer,
final Class<E> clazzE) {
return new RunOrContinuation<Out,E>(myProject, myTaskTitle, clazzE) {
@Override
protected Out calculate() {
return myCache.getValue(in);
}
@Override
protected Out calculateLong() {
protected Out calculateLong() throws E {
final Out result = myLive.convert(in);
if (result != null) {
myCache.setValue(result, in);
}
return result;
}
@Override
protected void processResult(Out out) {
resultConsumer.consume(out);
protected void processResult(TransparentlyFailedValueI<Out, E> t) {
resultConsumer.consume(t);
}
};
}
@CalledInAwt
public TaskDescriptor getTask(final In in, final Consumer<Out> resultConsumer) {
return createRunOrContinuation(in, resultConsumer).getTask();
public TaskDescriptor getTask(final In in, final Consumer<TransparentlyFailedValueI<Out, E>> resultConsumer, final Class<E> clazzE) {
return createRunOrContinuation(in, resultConsumer, clazzE).getTask();
}
}
@@ -191,8 +191,8 @@ public class QuickMerge {
finishWithError(context, e.getMessage(), true);
return;
}
context.next(myVcs.getSvnBranchPointsCalculator().getFirstCopyPointTask(myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl, calculator),
calculator);
context.next(myVcs.getSvnBranchPointsCalculator().getFirstCopyPointTask(
myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl, calculator), calculator);
}
}
@@ -254,42 +254,52 @@ public class QuickMerge {
queue.add(mergeAllExecutor);
}
private class MergeAllWithBranchCopyPoint extends TaskDescriptor implements Consumer<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>> {
private SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> myData;
private class MergeAllWithBranchCopyPoint extends TaskDescriptor implements Consumer<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>> {
private AtomicReference<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>> myData;
private MergeAllWithBranchCopyPoint() {
super("merge all", Where.AWT);
}
public void consume(SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> data) {
myData = data;
@Override
public void consume(TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException> value) {
myData.set(value);
}
@Override
public void run(ContinuationContext context) {
if (myData == null) {
SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> invertor;
try {
invertor = myData.get().get();
}
catch (SVNException e) {
finishWithError(context, "Merge start wasn't found", Collections.singletonList(new VcsException(e)));
return;
}
if (invertor == null) {
finishWithError(context, "Merge start wasn't found", true);
return;
}
final boolean reintegrate = myData.isInvertedSense();
final boolean reintegrate = invertor.isInvertedSense();
if (reintegrate && (! prompt("<html><body>You are going to reintegrate changes.<br><br>This will make branch '" + mySourceUrl +
"' <b>no longer usable for further work</b>." +
"<br>It will not be able to correctly absorb new trunk (" + myData.inverted().getTarget() +
"<br>It will not be able to correctly absorb new trunk (" + invertor.inverted().getTarget() +
") changes,<br>nor can this branch be properly reintegrated to trunk again.<br><br>Are you sure?</body></html>"))) {
context.cancelEverything();
return;
}
final MergerFactory mergerFactory = createBranchMergerFactory(reintegrate);
final MergerFactory mergerFactory = createBranchMergerFactory(reintegrate, invertor);
final String title = "Merging all from " + myBranchName + (reintegrate ? " (reintegrate)" : "");
context.next(new MergeTask(mergerFactory, title));
}
private MergerFactory createBranchMergerFactory(final boolean reintegrate) {
private MergerFactory createBranchMergerFactory(final boolean reintegrate,
final SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> invertor) {
return new MergerFactory() {
public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl, String branchName) {
return new BranchMerger(vcs, currentBranchUrl, myWcInfo.getUrl(), myWcInfo.getPath(), handler, reintegrate, myBranchName,
reintegrate ? myData.getWrapped().getTargetRevision() : myData.getWrapped().getSourceRevision());
reintegrate ? invertor.getWrapped().getTargetRevision() : invertor.getWrapped().getSourceRevision());
}
@Nullable
public List<CommittedChangeList> getListsToMerge() {
@@ -366,20 +376,24 @@ public class QuickMerge {
}
}
private class MergeCalculator extends TaskDescriptor implements Consumer<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>> {
private class MergeCalculator extends TaskDescriptor implements
Consumer<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>> {
private final static String ourOneShotStrategy = "svn.quickmerge.oneShotStrategy";
private final WCInfo myWcInfo;
private final String mySourceUrl;
private final String myBranchName;
private final AtomicReference<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>> myCopyData;
private final
AtomicReference<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>>
myCopyData;
private boolean myIsReintegrate;
private final List<CommittedChangeList> myNotMerged;
private String myMergeTitle;
private final MergeChecker myMergeChecker;
public void consume(SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> branchCopyDataWrapperInvertor) {
myCopyData.set(branchCopyDataWrapperInvertor);
@Override
public void consume(TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException> value) {
myCopyData.set(value);
}
private MergeCalculator(WCInfo wcInfo, String sourceUrl, String branchName) throws SVNException {
@@ -397,13 +411,20 @@ public class QuickMerge {
myWcInfo.getRootUrl(), mySourceUrl,
mySourceUrl, myVcs.createWCClient()));
}*/
myCopyData = new AtomicReference<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>>();
myCopyData = new AtomicReference<TransparentlyFailedValueI<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>, SVNException>>();
}
//"Calculating not merged revisions"
@Override
public void run(ContinuationContext context) {
SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> copyDataValue = myCopyData.get();
SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> copyDataValue = null;
try {
copyDataValue = myCopyData.get().get();
}
catch (SVNException e) {
finishWithError(context, "Merge start wasn't found", Collections.singletonList(new VcsException(e)));
return;
}
if (copyDataValue == null) {
finishWithError(context, "Merge start wasn't found", true);
return;
@@ -15,65 +15,89 @@
*/
package org.jetbrains.idea.svn.dialogs;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CalledInAwt;
import com.intellij.openapi.vcs.CalledInBackground;
import com.intellij.openapi.vcs.changes.ThreadSafeTransparentlyFailedValue;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.util.continuation.Continuation;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.atomic.AtomicReference;
public abstract class RunOrContinuation<T> {
public abstract class RunOrContinuation<T, E extends Exception> {
private final static Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.RunOrContinuation");
protected final Project myProject;
private final String myTaskTitle;
private final Class<E> myClazzE;
private volatile boolean myWasCanceled;
private TransparentlyFailedValueI<T,E> myTransparentlyFailedValue;
protected RunOrContinuation(final Project project, final String taskTitle) {
protected RunOrContinuation(final Project project, final String taskTitle, final Class<E> clazzE) {
myProject = project;
myTaskTitle = taskTitle;
myClazzE = clazzE;
myTransparentlyFailedValue = new ThreadSafeTransparentlyFailedValue<T, E>();
}
@Nullable
@CalledInAwt
protected abstract T calculate();
protected abstract T calculate() throws E;
@Nullable
@CalledInBackground
protected abstract T calculateLong();
protected abstract T calculateLong() throws E;
@CalledInAwt
protected abstract void processResult(final T t);
protected abstract void processResult(final TransparentlyFailedValueI<T, E> t);
protected void cancel() {
myWasCanceled = true;
}
private void setException(Exception e) {
if (myClazzE.isAssignableFrom(e.getClass())) {
myTransparentlyFailedValue.fail((E)e);
} else {
LOG.info(e);
myTransparentlyFailedValue.failRuntime((e instanceof RuntimeException ? (RuntimeException)e : new RuntimeException(e)));
}
}
@CalledInAwt
public TaskDescriptor getTask() {
final AtomicReference<T> refT = new AtomicReference<T>();
final TaskDescriptor pooled = new TaskDescriptor(myTaskTitle, Where.POOLED) {
@Override
public void run(ContinuationContext context) {
refT.set(calculateLong());
try {
myTransparentlyFailedValue.set(calculateLong());
}
catch (Exception e) {
setException(e);
}
if (! myWasCanceled) {
context.next(new TaskDescriptor("final part", Where.AWT) {
@Override
public void run(ContinuationContext context) {
processResult(refT.get());
processResult(myTransparentlyFailedValue);
}
});
}
}
};
return new TaskDescriptor("short part", Where.AWT) {
@Override
public void run(ContinuationContext context) {
refT.set(calculate());
if ((! myWasCanceled) && (refT.get() != null)) {
processResult(refT.get());
try {
myTransparentlyFailedValue.set(calculate());
}
catch (Exception e) {
setException(e);
}
if ((! myWasCanceled) && (myTransparentlyFailedValue.haveSomething())) {
processResult(myTransparentlyFailedValue);
return;
}
context.next(pooled);
@@ -85,25 +109,4 @@ public abstract class RunOrContinuation<T> {
public void execute() {
Continuation.createFragmented(myProject, true).run(getTask());
}
/*@CalledInAwt
public void execute() {
final Ref<T> refT = new Ref<T>();
refT.set(calculate());
if ((! myWasCanceled) && (! refT.isNull())) {
processResult(refT.get());
return;
}
ProgressManager.getInstance().run(new Task.Backgroundable(myProject, myTaskTitle, true, BackgroundFromStartOption.getInstance()) {
public void run(@NotNull ProgressIndicator indicator) {
refT.set(calculateLong());
}
@Override
public void onSuccess() {
if (! myWasCanceled) {
processResult(refT.get());
}
}
});
} */
}
@@ -18,21 +18,21 @@ package org.jetbrains.idea.svn.dialogs;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.openapi.vcs.persistent.SmallMapSerializer;
import com.intellij.util.Consumer;
import com.intellij.util.ThrowableConvertor;
import com.intellij.util.ValueHolder;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.EnumeratorStringDescriptor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.history.CopyData;
import org.jetbrains.idea.svn.history.FirstInBranch;
import org.tmatesoft.svn.core.SVNException;
import java.io.DataInput;
import java.io.DataOutput;
@@ -42,7 +42,7 @@ import java.util.*;
public class SvnBranchPointsCalculator {
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator");
private FactsCalculator<KeyData, WrapperInvertor<BranchCopyData>> myCalculator;
private FactsCalculator<KeyData, WrapperInvertor<BranchCopyData>, SVNException> myCalculator;
private PersistentHolder myPersistentHolder;
private File myFile;
private final Project myProject;
@@ -77,7 +77,8 @@ public class SvnBranchPointsCalculator {
}
};
myCalculator = new FactsCalculator<KeyData, WrapperInvertor<BranchCopyData>>(myProject, "Looking for branch origin", cache, new Loader(myProject));
myCalculator = new FactsCalculator<KeyData, WrapperInvertor<BranchCopyData>, SVNException>(
myProject, "Looking for branch origin", cache, new Loader(myProject));
}
public void deactivate() {
@@ -238,38 +239,39 @@ public class SvnBranchPointsCalculator {
}
}
private static class Loader implements Convertor<KeyData, WrapperInvertor<BranchCopyData>> {
private static class Loader implements ThrowableConvertor<KeyData, WrapperInvertor<BranchCopyData>, SVNException> {
private SvnVcs myVcs;
private Loader(final Project project) {
myVcs = SvnVcs.getInstance(project);
}
public WrapperInvertor<BranchCopyData> convert(final KeyData keyData) {
final Ref<WrapperInvertor<BranchCopyData>> result = new Ref<WrapperInvertor<BranchCopyData>>();
@Override
public WrapperInvertor<BranchCopyData> convert(final KeyData keyData) throws SVNException {
final TransparentlyFailedValue<CopyData, SVNException> consumer = new TransparentlyFailedValue<CopyData, SVNException>();
new FirstInBranch(myVcs, keyData.getRepoUrl(), keyData.getTargetUrl(), keyData.getSourceUrl(), consumer).run();
new FirstInBranch(myVcs, keyData.getRepoUrl(), keyData.getTargetUrl(), keyData.getSourceUrl(), new Consumer<CopyData>() {
public void consume(CopyData copyData) {
if (copyData != null) {
final boolean correct = copyData.isTrunkSupposedCorrect();
final BranchCopyData branchCopyData;
if (correct) {
branchCopyData = new BranchCopyData(keyData.getSourceUrl(), copyData.getCopySourceRevision(), keyData.getTargetUrl(),
copyData.getCopyTargetRevision());
} else {
branchCopyData = new BranchCopyData(keyData.getTargetUrl(), copyData.getCopySourceRevision(), keyData.getSourceUrl(),
copyData.getCopyTargetRevision());
}
result.set(new WrapperInvertor<BranchCopyData>(! correct, branchCopyData));
}
final CopyData copyData = consumer.get();
if (copyData != null) {
final boolean correct = copyData.isTrunkSupposedCorrect();
final BranchCopyData branchCopyData;
if (correct) {
branchCopyData = new BranchCopyData(keyData.getSourceUrl(), copyData.getCopySourceRevision(), keyData.getTargetUrl(),
copyData.getCopyTargetRevision());
} else {
branchCopyData = new BranchCopyData(keyData.getTargetUrl(), copyData.getCopySourceRevision(), keyData.getSourceUrl(),
copyData.getCopyTargetRevision());
}
}).run();
final WrapperInvertor<BranchCopyData> invertor = result.get();
if (LOG.isDebugEnabled()) {
LOG.debug("Loader17 returned: for key: " + keyData.toString() + " result: " + (invertor == null ? null : invertor.toString()));
WrapperInvertor<BranchCopyData> invertor = new WrapperInvertor<BranchCopyData>(! correct, branchCopyData);
if (LOG.isDebugEnabled()) {
LOG.debug("Loader17 returned: for key: " + keyData.toString() + " result: " + (invertor.toString()));
}
return invertor;
}
return invertor;
if (LOG.isDebugEnabled()) {
LOG.debug("Loader17 returned: for key: " + keyData.toString() + " result: null");
}
return null;
}
}
@@ -345,18 +347,8 @@ public class SvnBranchPointsCalculator {
T invertSelf();
}
public void getFirstCopyPoint(final String repoUID, final String sourceUrl, final String targetUrl, Consumer<WrapperInvertor<BranchCopyData>> consumer) {
myCalculator.get(new KeyData(repoUID, sourceUrl, targetUrl), consumer);
}
public TaskDescriptor getFirstCopyPointTask(final String repoUID, final String sourceUrl, final String targetUrl,
final Consumer<WrapperInvertor<BranchCopyData>> consumer) {
return myCalculator.getTask(new KeyData(repoUID, sourceUrl, targetUrl), consumer);
}
public static abstract class CopyPointAcceptorTask extends TaskDescriptor implements Consumer<WrapperInvertor<BranchCopyData>> {
protected CopyPointAcceptorTask(String name, @NotNull Where where) {
super(name, where);
}
final Consumer<TransparentlyFailedValueI<WrapperInvertor<BranchCopyData>, SVNException>> consumer) {
return myCalculator.getTask(new KeyData(repoUID, sourceUrl, targetUrl), consumer, SVNException.class);
}
}
@@ -16,11 +16,11 @@
package org.jetbrains.idea.svn.history;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.vcs.ConcurrentTasks;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.util.Consumer;
import com.intellij.util.containers.hash.HashSet;
import org.jetbrains.idea.svn.SvnVcs;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
@@ -28,21 +28,22 @@ import org.tmatesoft.svn.core.wc.SVNLogClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
import java.util.Map;
import java.util.Set;
public class FirstInBranch implements Runnable {
private final SvnVcs myVcs;
private final String myFullBranchUrl;
private final String myFullTrunkUrl;
private final String myBranchUrl;
private final String myTrunkUrl;
private final Consumer<CopyData> myConsumer;
private final String myRepositoryRoot;
private final TransparentlyFailedValueI<CopyData, SVNException> myConsumer;
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.history.FirstInBranch");
public FirstInBranch(final SvnVcs vcs, final String repositoryRoot, final String branchUrl, final String trunkUrl, final Consumer<CopyData> consumer) {
public FirstInBranch(final SvnVcs vcs, final String repositoryRoot, final String branchUrl, final String trunkUrl,
final TransparentlyFailedValueI<CopyData, SVNException> consumer) {
myVcs = vcs;
myRepositoryRoot = repositoryRoot;
myConsumer = consumer;
myFullBranchUrl = branchUrl;
myFullTrunkUrl = trunkUrl;
myBranchUrl = relativePath(repositoryRoot, branchUrl);
myTrunkUrl = relativePath(repositoryRoot, trunkUrl);
}
@@ -53,28 +54,37 @@ public class FirstInBranch implements Runnable {
}
public void run() {
final SVNURL branchURL;
final SVNURL trunkURL;
final Set<SVNException> exceptions = new HashSet<SVNException>();
final boolean [] called = new boolean[1];
try {
branchURL = SVNURL.parseURIEncoded(myFullBranchUrl);
trunkURL = SVNURL.parseURIEncoded(myFullTrunkUrl);
createTask(SVNURL.parseURIDecoded(myRepositoryRoot), exceptions).consume(new Consumer<CopyData>() {
@Override
public void consume(CopyData data) {
if (data != null) {
myConsumer.set(data);
called[0] = true;
}
}
});
}
catch (SVNException e) {
myConsumer.consume(null);
myConsumer.fail(e);
return;
}
if (called[0]) return;
final ConcurrentTasks<CopyData> tasks =
new ConcurrentTasks<CopyData>(ProgressManager.getInstance().getProgressIndicator(), createTask(branchURL), createTask(trunkURL));
tasks.compute();
if (tasks.isResultKnown()) {
myConsumer.consume(tasks.getResult());
} else {
myConsumer.consume(null);
if (! exceptions.isEmpty()) {
LOG.info("Wasn't able to find branch point, exception(s) below");
for (SVNException exception : exceptions) {
LOG.info(exception);
}
myConsumer.fail(exceptions.iterator().next());
} else if (! called[0]) {
myConsumer.set(null);
}
}
private Consumer<Consumer<CopyData>> createTask(final SVNURL branchURL) {
private Consumer<Consumer<CopyData>> createTask(final SVNURL branchURL, final Set<SVNException> exceptions) {
return new Consumer<Consumer<CopyData>>() {
public void consume(final Consumer<CopyData> copyDataConsumer) {
final SVNLogClient logClient = ApplicationManager.getApplication().runReadAction(new Computable<SVNLogClient>() {
@@ -85,59 +95,67 @@ public class FirstInBranch implements Runnable {
}
});
if (logClient == null) return;
final long start1 = getStart(logClient, branchURL);
if (start1 > 0) {
final SVNRevision start1Rev = SVNRevision.create(start1);
try {
logClient.doLog(branchURL, null, SVNRevision.UNDEFINED, start1Rev, start1Rev, true, true, 1, new ISVNLogEntryHandler() {
public void handleLogEntry(SVNLogEntry logEntry) throws SVNException {
checkForCopy(logEntry, copyDataConsumer);
}
});
} catch (SVNException e) {
//
}
try {
logClient.doLog(branchURL, null, SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNRevision.create(0), false, true, -1,
new MyLogEntryHandler(copyDataConsumer, myTrunkUrl, myBranchUrl));
} catch (SVNCancelException e) {
//
} catch (SVNException e) {
exceptions.add(e);
}
}
};
}
private static long getStart(final SVNLogClient logClient, final SVNURL url) {
final Ref<Long> myRevisionCandidate = new Ref<Long>(0L);
try {
logClient.doLog(url, null, SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNRevision.create(0),
true, false, 0, new ISVNLogEntryHandler() {
public void handleLogEntry(SVNLogEntry logEntry) throws SVNException {
ProgressManager.checkCanceled();
private static class MyLogEntryHandler implements ISVNLogEntryHandler {
private final SvnPathThroughHistoryCorrection myTrunkCorrector;
private final SvnPathThroughHistoryCorrection myBranchCorrector;
private final Consumer<CopyData> myCopyDataConsumer;
myRevisionCandidate.set(logEntry.getRevision());
}
});
public MyLogEntryHandler(Consumer<CopyData> copyDataConsumer, String trunkUrl, String branchUrl) {
myCopyDataConsumer = copyDataConsumer;
myTrunkCorrector = new SvnPathThroughHistoryCorrection(trunkUrl);
myBranchCorrector = new SvnPathThroughHistoryCorrection(branchUrl);
}
catch (SVNException e) {
//
public void handleLogEntry(SVNLogEntry logEntry) throws SVNException {
final Map map = logEntry.getChangedPaths();
checkEntries(logEntry, map);
myTrunkCorrector.handleLogEntry(logEntry);
myBranchCorrector.handleLogEntry(logEntry);
checkEntries(logEntry, map);
}
return myRevisionCandidate.get();
}
private void checkForCopy(final SVNLogEntry logEntry, final Consumer<CopyData> result) {
final Map map = logEntry.getChangedPaths();
for (Object o : map.values()) {
final SVNLogEntryPath path = (SVNLogEntryPath) o;
final String localPath = path.getPath();
final String copyPath = path.getCopyPath();
private void checkEntries(SVNLogEntry logEntry, Map map) throws SVNCancelException {
for (Object o : map.values()) {
final SVNLogEntryPath path = (SVNLogEntryPath) o;
final String localPath = path.getPath();
final String copyPath = path.getCopyPath();
if ('A' == path.getType()) {
if ((myBranchUrl.equals(localPath) || SVNPathUtil.isAncestor(localPath, myBranchUrl)) &&
((myTrunkUrl.equals(copyPath)) || SVNPathUtil.isAncestor(copyPath, myTrunkUrl))) {
result.consume(new CopyData(path.getCopyRevision(), logEntry.getRevision(), true));
} else {
if ((myBranchUrl.equals(copyPath) || SVNPathUtil.isAncestor(copyPath, myBranchUrl)) &&
((myTrunkUrl.equals(localPath)) || SVNPathUtil.isAncestor(localPath, myTrunkUrl))) {
result.consume(new CopyData(path.getCopyRevision(), logEntry.getRevision(), false));
if ('A' == path.getType()) {
if (checkForCopyCase(logEntry, path, localPath, copyPath, myTrunkCorrector.getCurrentPath(), myBranchCorrector.getCurrentPath())) {
throw new SVNCancelException();
}
}
}
}
private boolean checkForCopyCase(SVNLogEntry logEntry, SVNLogEntryPath 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);
}
}
}
@@ -56,7 +56,6 @@ import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class SvnHistoryProvider
implements VcsHistoryProvider, VcsCacheableHistorySessionFactory<Boolean, SvnHistorySession> {
@@ -476,7 +475,7 @@ public class SvnHistoryProvider
private static class MyLogEntryHandler implements ISVNLogEntryHandler {
private final ProgressIndicator myIndicator;
protected final SvnVcs myVcs;
protected String myLastPath;
protected final SvnPathThroughHistoryCorrection myLastPathCorrector;
private final Charset myCharset;
protected final ThrowableConsumer<VcsFileRevision, SVNException> myResult;
private VcsFileRevision myPrevious;
@@ -497,7 +496,7 @@ public class SvnHistoryProvider
SVNURL repoRootURL, Charset charset)
throws SVNException, VcsException {
myVcs = vcs;
myLastPath = lastPath;
myLastPathCorrector = new SvnPathThroughHistoryCorrection(lastPath);
myCharset = charset;
myIndicator = ProgressManager.getInstance().getProgressIndicator();
myResult = result;
@@ -514,24 +513,19 @@ public class SvnHistoryProvider
}
myIndicator.setText2(SvnBundle.message("progress.text2.revision.processed", logEntry.getRevision()));
}
myLastPathCorrector.handleLogEntry(logEntry);
SVNLogEntryPath entryPath = myLastPathCorrector.getDirectlyMentioned();
String copyPath = null;
SVNLogEntryPath entryPath = (SVNLogEntryPath)logEntry.getChangedPaths().get(myLastPath);
if (entryPath != null) {
copyPath = entryPath.getCopyPath();
} else {
// if there are no path with exact match, check whether parent or child paths had changed
// "entry path" is allowed to be null now; if it is null, last pa in th would be taken for revision construction
// if parent path was renamed, last path would be corrected below in correctLastPathAccordingToFolderRenames
// "entry path" is allowed to be null now; if it is null, last path would be taken for revision construction
if (! checkForChildChanges(logEntry) && ! checkForParentChanges(logEntry)) return;
}
final int mergeLevel = svnLogEntryIntegerPair.getSecond();
final SvnFileRevision revision = createRevision(logEntry, copyPath, entryPath);
if (copyPath != null) {
myLastPath = copyPath;
} else if (entryPath == null) {
myLastPath = correctLastPathAccordingToFolderRenames(myLastPath, logEntry);
}
if (mergeLevel >= 0) {
addToListByLevel((SvnFileRevision)myPrevious, revision, mergeLevel);
}
@@ -548,12 +542,12 @@ public class SvnHistoryProvider
}
private boolean checkForParentChanges(SVNLogEntry logEntry) {
String relativePath = null;
String path = SVNPathUtil.removeTail(myLastPath);
final String lastPathBefore = myLastPathCorrector.getBefore();
String path = SVNPathUtil.removeTail(lastPathBefore);
while (path.length() > 0) {
final SVNLogEntryPath entryPath = (SVNLogEntryPath)logEntry.getChangedPaths().get(path);
final SVNLogEntryPath entryPath = logEntry.getChangedPaths().get(path);
// A & D are checked since we are not interested in parent folders property changes, only in structure changes
if (entryPath != null && (entryPath.getType() == 'A' || entryPath.getType() == 'D')) {
relativePath = SVNPathUtil.getRelativePath(entryPath.getPath(), myLastPath);
if (entryPath.getCopyPath() != null) {
return true;
}
@@ -565,30 +559,15 @@ public class SvnHistoryProvider
}
private boolean checkForChildChanges(SVNLogEntry logEntry) {
final String lastPathBefore = myLastPathCorrector.getBefore();
for (String key : logEntry.getChangedPaths().keySet()) {
if (SVNPathUtil.isAncestor(myLastPath, key)) {
if (SVNPathUtil.isAncestor(lastPathBefore, key)) {
return true;
}
}
return false;
}
private String correctLastPathAccordingToFolderRenames(String lastPath, SVNLogEntry logEntry) {
final Map<String,SVNLogEntryPath> paths = logEntry.getChangedPaths();
for (Map.Entry<String, SVNLogEntryPath> entry : paths.entrySet()) {
final SVNLogEntryPath value = entry.getValue();
final String copyPath = value.getCopyPath();
if (copyPath != null) {
final String entryPath = value.getPath();
if (SVNPathUtil.isAncestor(entryPath, lastPath)) {
final String relativePath = SVNPathUtil.getRelativePath(entryPath, lastPath);
return SVNPathUtil.append(copyPath, relativePath);
}
}
}
return lastPath;
}
public void handleLogEntry(SVNLogEntry logEntry) throws SVNException {
myTracker.consume(logEntry);
}
@@ -614,7 +593,7 @@ public class SvnHistoryProvider
SVNRevision rev = SVNRevision.create(logEntry.getRevision());
// final SVNURL url = myRepositoryRoot.appendPath(myLastPath, true);
final SVNURL url = entryPath != null ? myRepositoryRoot.appendPath(entryPath.getPath(), true) :
myRepositoryRoot.appendPath(myLastPath, true);
myRepositoryRoot.appendPath(myLastPathCorrector.getBefore(), false);
return new SvnFileRevision(myVcs, myPegRevision, rev, url.toString(), author, date, message, copyPath, myCharset);
}
}
@@ -632,7 +611,7 @@ public class SvnHistoryProvider
@Override
protected SvnFileRevision createRevision(final SVNLogEntry logEntry, final String copyPath, SVNLogEntryPath entryPath)
throws SVNException {
final SVNURL url = entryPath == null ? myRepositoryRoot.appendPath(myLastPath, true) :
final SVNURL url = entryPath == null ? myRepositoryRoot.appendPath(myLastPathCorrector.getBefore(), false) :
myRepositoryRoot.appendPath(entryPath.getPath(), true);
return new SvnFileRevision(myVcs, SVNRevision.UNDEFINED, logEntry, url.toString(), copyPath, null);
}
@@ -0,0 +1,91 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.idea.svn.history;
import org.jetbrains.idea.svn.SvnUtil;
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.internal.util.SVNPathUtil;
import java.util.Map;
/**
* Created with IntelliJ IDEA.
* User: Irina.Chernushina
* Date: 8/30/12
* Time: 5:18 PM
*
* We consider here, that history is traversed "from now to past"
*/
public class SvnPathThroughHistoryCorrection implements ISVNLogEntryHandler {
private String myBefore;
private String myPath;
private SVNLogEntryPath myDirectlyMentioned;
public SvnPathThroughHistoryCorrection(String path) {
myPath = path;
myBefore = path;
}
@Override
public void handleLogEntry(SVNLogEntry logEntry) throws SVNException {
myBefore = myPath;
myDirectlyMentioned = null;
final Map<String,SVNLogEntryPath> paths = logEntry.getChangedPaths();
final SVNLogEntryPath entryPath = paths.get(myPath);
if (entryPath != null) {
myDirectlyMentioned = entryPath;
// exact match
if (entryPath.getCopyPath() != null) {
myPath = entryPath.getCopyPath();
return;
}
}
for (SVNLogEntryPath path : paths.values()) {
// "the origin path *from where* the item, ..."
final String copyPath = path.getCopyPath();
if (copyPath != null) {
final String thisEntryPath = path.getPath();
if (parentPathChanged(copyPath, thisEntryPath)) {
return;
}
}
}
}
private boolean parentPathChanged(String copyPath, String thisEntryPath) throws SVNException {
if (SVNPathUtil.isAncestor(thisEntryPath, myPath)) {
final String relativePath = SVNPathUtil.getRelativePath(thisEntryPath, myPath);
myPath = SvnUtil.appendMultiParts(copyPath, relativePath);
return true;
}
return false;
}
public String getBefore() {
return myBefore;
}
public SVNLogEntryPath getDirectlyMentioned() {
return myDirectlyMentioned;
}
public String getCurrentPath() {
return myPath;
}
}
@@ -19,7 +19,10 @@ import com.intellij.lifecycle.PeriodicalTasksCloser;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.util.Consumer;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValue;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.openapi.vcs.ui.VcsBalloonProblemNotifier;
import com.intellij.util.containers.SoftHashMap;
import com.intellij.util.messages.Topic;
import org.jetbrains.annotations.Nullable;
@@ -29,6 +32,7 @@ import org.jetbrains.idea.svn.dialogs.WCPaths;
import org.jetbrains.idea.svn.history.CopyData;
import org.jetbrains.idea.svn.history.FirstInBranch;
import org.jetbrains.idea.svn.history.SvnChangeList;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.wc.SVNWCClient;
@@ -167,21 +171,34 @@ public class SvnMergeInfoCache {
myPath = path;
myRevision = -1;
ApplicationManager.getApplication().executeOnPooledThread(new FirstInBranch(vcs, repositoryRoot, branchUrl, trunkUrl,
new Consumer<CopyData>() {
public void consume(CopyData copyData) {
if (copyData == null) return;
myRevision = copyData.getCopySourceRevision();
if (myRevision != -1) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
if (vcs.getProject().isDisposed()) return;
vcs.getProject().getMessageBus().syncPublisher(SVN_MERGE_INFO_CACHE).copyRevisionUpdated();
}
});
}
}
}));
final TransparentlyFailedValueI<CopyData, SVNException> result = new TransparentlyFailedValue<CopyData, SVNException>() {
@Override
public void set(CopyData copyData) {
if (copyData == null) return;
myRevision = copyData.getCopySourceRevision();
if (myRevision != -1) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
if (vcs.getProject().isDisposed()) return;
vcs.getProject().getMessageBus().syncPublisher(SVN_MERGE_INFO_CACHE).copyRevisionUpdated();
}
});
}
}
@Override
public void fail(SVNException e) {
LOG.info(e);
VcsBalloonProblemNotifier.showOverChangesView(vcs.getProject(), e.getMessage(), MessageType.ERROR);
}
@Override
public void failRuntime(RuntimeException e) {
LOG.info(e);
VcsBalloonProblemNotifier.showOverChangesView(vcs.getProject(), e.getMessage(), MessageType.ERROR);
}
};
ApplicationManager.getApplication().executeOnPooledThread(new FirstInBranch(vcs, repositoryRoot, branchUrl, trunkUrl, result));
}
public String getPath() {
@@ -17,18 +17,21 @@ package org.jetbrains.idea.svn.treeConflict;
import com.intellij.CommonBundle;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.diff.impl.patch.*;
import com.intellij.openapi.fileChooser.*;
import com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP;
import com.intellij.openapi.diff.impl.patch.FilePatch;
import com.intellij.openapi.diff.impl.patch.PatchSyntaxException;
import com.intellij.openapi.diff.impl.patch.TextFilePatch;
import com.intellij.openapi.fileChooser.FileChooserFactory;
import com.intellij.openapi.fileChooser.FileSaverDescriptor;
import com.intellij.openapi.fileChooser.FileSaverDialog;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FilePathImpl;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.changes.*;
import com.intellij.openapi.vcs.changes.patch.ApplyPatchDefaultExecutor;
import com.intellij.openapi.vcs.changes.CommitContext;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import com.intellij.openapi.vcs.changes.TransparentlyFailedValueI;
import com.intellij.openapi.vcs.changes.patch.ApplyPatchExecutor;
import com.intellij.openapi.vcs.changes.patch.FilePatchInProgress;
import com.intellij.openapi.vcs.changes.patch.PatchWriter;
@@ -71,7 +74,7 @@ public class ApplyPatchSaveToFileExecutor implements ApplyPatchExecutor {
public void apply(MultiMap<VirtualFile, FilePatchInProgress> patchGroups,
LocalChangeList localList,
String fileName,
TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
final FileSaverDialog dialog = FileChooserFactory.getInstance().createSaveFileDialog(
new FileSaverDescriptor("Save patch to", ""), myProject);
final VirtualFile baseDir = myProject.getBaseDir();
@@ -45,7 +45,10 @@ import com.intellij.util.Consumer;
import com.intellij.util.SmartList;
import com.intellij.util.containers.Convertor;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.continuation.*;
import com.intellij.util.continuation.Continuation;
import com.intellij.util.continuation.ContinuationContext;
import com.intellij.util.continuation.TaskDescriptor;
import com.intellij.util.continuation.Where;
import org.jetbrains.idea.svn.*;
import org.jetbrains.idea.svn.history.SvnChangeList;
import org.jetbrains.idea.svn.history.SvnRepositoryLocation;
@@ -249,7 +252,7 @@ public class MergeFromTheirsResolver {
@Override
public void apply(MultiMap<VirtualFile, FilePatchInProgress> patchGroups, LocalChangeList localList, String fileName,
TransparentlyFailedValue<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
TransparentlyFailedValueI<Map<String, Map<String, CharSequence>>, PatchSyntaxException> additionalInfo) {
final List<FilePatch> patches;
try {
patches = ApplyPatchSaveToFileExecutor.patchGroupsToOneGroup(patchGroups, myBaseDir);