From 8e07bfc53513b2e0b490e5aa84cebe7118482f75 Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 3 Sep 2012 18:08:15 +0400 Subject: [PATCH] IDEA-75800 SVN "Merge from..." not working --- .../openapi/diff/impl/patch/PatchReader.java | 3 +- .../intellij/openapi/vcs/ConcurrentTasks.java | 2 +- .../ThreadSafeTransparentlyFailedValue.java | 100 ++++++++++++ .../vcs/changes/TransparentlyFailedValue.java | 18 ++- .../changes/TransparentlyFailedValueI.java | 34 +++++ .../patch/ApplyPatchDefaultExecutor.java | 10 +- .../vcs/changes/patch/ApplyPatchExecutor.java | 4 +- .../changes/patch/ImportToShelfExecutor.java | 4 +- .../changes/shelf/ShelveChangesManager.java | 2 +- .../src/org/jetbrains/idea/svn/SvnUtil.java | 13 +- .../idea/svn/dialogs/FactsCalculator.java | 29 ++-- .../idea/svn/dialogs/QuickMerge.java | 57 ++++--- .../idea/svn/dialogs/RunOrContinuation.java | 73 ++++----- .../dialogs/SvnBranchPointsCalculator.java | 74 ++++----- .../idea/svn/history/FirstInBranch.java | 142 ++++++++++-------- .../idea/svn/history/SvnHistoryProvider.java | 47 ++---- .../SvnPathThroughHistoryCorrection.java | 91 +++++++++++ .../idea/svn/mergeinfo/SvnMergeInfoCache.java | 49 ++++-- .../ApplyPatchSaveToFileExecutor.java | 19 ++- .../treeConflict/MergeFromTheirsResolver.java | 7 +- 20 files changed, 535 insertions(+), 243 deletions(-) create mode 100644 platform/vcs-api/src/com/intellij/openapi/vcs/changes/ThreadSafeTransparentlyFailedValue.java create mode 100644 platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValueI.java create mode 100644 plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnPathThroughHistoryCorrection.java diff --git a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java index 4e6c3412efe7..945da5978024 100644 --- a/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java +++ b/platform/vcs-api/src/com/intellij/openapi/diff/impl/patch/PatchReader.java @@ -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>, PatchSyntaxException> getAdditionalInfo(final Set filterByPaths) { + public TransparentlyFailedValueI>, PatchSyntaxException> getAdditionalInfo(final Set filterByPaths) { final TransparentlyFailedValue>, PatchSyntaxException> value = new TransparentlyFailedValue>, PatchSyntaxException>(); diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/ConcurrentTasks.java b/platform/vcs-api/src/com/intellij/openapi/vcs/ConcurrentTasks.java index f38adc74ab98..f2b0bcb3a929 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/ConcurrentTasks.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/ConcurrentTasks.java @@ -53,7 +53,7 @@ public class ConcurrentTasks { final List> futures = new LinkedList>(); for (final Consumer> task : myTasks) { if (myResultKnown) { - myCntAlive.decrementAndGet(); + final int decremented = myCntAlive.decrementAndGet(); continue; } final Runnable computableProxy = new Runnable() { diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ThreadSafeTransparentlyFailedValue.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ThreadSafeTransparentlyFailedValue.java new file mode 100644 index 000000000000..ddc6c7ce6213 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ThreadSafeTransparentlyFailedValue.java @@ -0,0 +1,100 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.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 implements TransparentlyFailedValueI { + private final AtomicReference> myRef; + + public ThreadSafeTransparentlyFailedValue() { + myRef = new AtomicReference>(); + } + + @Override + public void set(T t) { + if (t != null) { + myRef.set(new Value(t)); + } + } + + @Override + public void fail(E e) { + myRef.set(new ExceptionHolder(e)); + } + + @Override + public void failRuntime(RuntimeException e) { + myRef.set(new RuntimeExceptionHolder(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 implements ThrowableComputable { + private final T myT; + + private Value(final T t) { + myT = t; + } + + @Override + public T compute() throws E { + return myT; + } + } + + private static class ExceptionHolder implements ThrowableComputable { + private final E myE; + + private ExceptionHolder(E e) { + myE = e; + } + + @Override + public T compute() throws E { + throw myE; + } + } + + private static class RuntimeExceptionHolder implements ThrowableComputable { + private final RuntimeException myException; + + private RuntimeExceptionHolder(RuntimeException exception) { + myException = exception; + } + + @Override + public T compute() throws E { + throw myException; + } + } +} diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValue.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValue.java index d34da8fd984a..5ab8d9609087 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValue.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValue.java @@ -20,25 +20,41 @@ package com.intellij.openapi.vcs.changes; * Date: 7/5/11 * Time: 3:35 PM */ -public class TransparentlyFailedValue { +public class TransparentlyFailedValue implements TransparentlyFailedValueI { 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 value) { this.t = value.t; this.e = value.e; + myRuntime = value.myRuntime; + } + + @Override + public boolean haveSomething() { + return e != null || myRuntime != null || t != null; } } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValueI.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValueI.java new file mode 100644 index 000000000000..7c867d6cbbd4 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/TransparentlyFailedValueI.java @@ -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 { + void set(T t); + + void fail(E e); + + T get() throws E; + + void failRuntime(RuntimeException e); + + boolean haveSomething(); +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDefaultExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDefaultExecutor.java index fd09028d5903..fc26b3b1eb02 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDefaultExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchDefaultExecutor.java @@ -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 patchGroups, LocalChangeList localList, String fileName, - TransparentlyFailedValue>, PatchSyntaxException> additionalInfo) { + TransparentlyFailedValueI>, PatchSyntaxException> additionalInfo) { final Collection appliers = new LinkedList(); 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>, PatchSyntaxException> additionalInfo, + TransparentlyFailedValueI>, PatchSyntaxException> additionalInfo, CommitContext commitContext) { applyAdditionalInfoImpl(project, additionalInfo, commitContext, new Consumer() { @Override @@ -93,7 +93,7 @@ public class ApplyPatchDefaultExecutor implements ApplyPatchExecutor { } private static void applyAdditionalInfo(final Project project, - TransparentlyFailedValue>, PatchSyntaxException> additionalInfo, + TransparentlyFailedValueI>, PatchSyntaxException> additionalInfo, CommitContext commitContext) { applyAdditionalInfoImpl(project, additionalInfo, commitContext, new Consumer() { @Override @@ -104,7 +104,7 @@ public class ApplyPatchDefaultExecutor implements ApplyPatchExecutor { } public static void applyAdditionalInfoImpl(final Project project, - TransparentlyFailedValue>, PatchSyntaxException> additionalInfo, + TransparentlyFailedValueI>, PatchSyntaxException> additionalInfo, CommitContext commitContext, final Consumer worker) { final PatchEP[] extensions = Extensions.getExtensions(PatchEP.EP_NAME, project); if (extensions == null && extensions.length == 0) return; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchExecutor.java index 4166204b9060..268d8decaa46 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ApplyPatchExecutor.java @@ -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 patchGroups, final LocalChangeList localList, String fileName, - TransparentlyFailedValue>, PatchSyntaxException> additionalInfo); + TransparentlyFailedValueI>, PatchSyntaxException> additionalInfo); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ImportToShelfExecutor.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ImportToShelfExecutor.java index 3afeee40a174..79d177e49f35 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ImportToShelfExecutor.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/patch/ImportToShelfExecutor.java @@ -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 patchGroups, LocalChangeList localList, final String fileName, - final TransparentlyFailedValue>, PatchSyntaxException> additionalInfo) { + final TransparentlyFailedValueI>, PatchSyntaxException> additionalInfo) { final VcsCatchingRunnable vcsCatchingRunnable = new VcsCatchingRunnable() { @Override public void runImpl() throws VcsException { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java index 474980a5b5fd..d7f87939a954 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelveChangesManager.java @@ -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 textFilePatches = reader.readAllPatches(); - final TransparentlyFailedValue>, PatchSyntaxException> additionalInfo = reader.getAdditionalInfo( + final TransparentlyFailedValueI>, PatchSyntaxException> additionalInfo = reader.getAdditionalInfo( null); ApplyPatchDefaultExecutor.applyAdditionalInfoBefore(project, additionalInfo, commitContext); return textFilePatches; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java index a8ccd4999850..840f34ae23aa 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/SvnUtil.java @@ -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 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 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; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/FactsCalculator.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/FactsCalculator.java index 6937ba5277d1..df5cee160a99 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/FactsCalculator.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/FactsCalculator.java @@ -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 { +public class FactsCalculator { private final Project myProject; private final String myTaskTitle; private final ValueHolder myCache; - private final Convertor myLive; + private final ThrowableConvertor myLive; - public FactsCalculator(Project project, String taskTitle, ValueHolder cache, Convertor live) { + public FactsCalculator(Project project, String taskTitle, ValueHolder cache, ThrowableConvertor live) { myProject = project; myTaskTitle = taskTitle; myCache = cache; @@ -37,33 +38,35 @@ public class FactsCalculator { } @CalledInAwt - public void get(final In in, final Consumer resultConsumer) { - createRunOrContinuation(in, resultConsumer).execute(); + public void get(final In in, final Consumer> resultConsumer, final Class clazzE) { + createRunOrContinuation(in, resultConsumer, clazzE).execute(); } - private RunOrContinuation createRunOrContinuation(final In in, final Consumer resultConsumer) { - return new RunOrContinuation(myProject, myTaskTitle) { + private RunOrContinuation createRunOrContinuation(final In in, final Consumer> resultConsumer, + final Class clazzE) { + return new RunOrContinuation(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 t) { + resultConsumer.consume(t); } }; } @CalledInAwt - public TaskDescriptor getTask(final In in, final Consumer resultConsumer) { - return createRunOrContinuation(in, resultConsumer).getTask(); + public TaskDescriptor getTask(final In in, final Consumer> resultConsumer, final Class clazzE) { + return createRunOrContinuation(in, resultConsumer, clazzE).getTask(); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java index 6fe6920f1c26..765bef5a455b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/QuickMerge.java @@ -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> { - private SvnBranchPointsCalculator.WrapperInvertor myData; + private class MergeAllWithBranchCopyPoint extends TaskDescriptor implements Consumer, SVNException>> { + private AtomicReference, SVNException>> myData; private MergeAllWithBranchCopyPoint() { super("merge all", Where.AWT); } - public void consume(SvnBranchPointsCalculator.WrapperInvertor data) { - myData = data; + @Override + public void consume(TransparentlyFailedValueI, SVNException> value) { + myData.set(value); } @Override public void run(ContinuationContext context) { - if (myData == null) { + SvnBranchPointsCalculator.WrapperInvertor 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("You are going to reintegrate changes.

This will make branch '" + mySourceUrl + "' no longer usable for further work." + - "
It will not be able to correctly absorb new trunk (" + myData.inverted().getTarget() + + "
It will not be able to correctly absorb new trunk (" + invertor.inverted().getTarget() + ") changes,
nor can this branch be properly reintegrated to trunk again.

Are you sure?"))) { 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 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 getListsToMerge() { @@ -366,20 +376,24 @@ public class QuickMerge { } } - private class MergeCalculator extends TaskDescriptor implements Consumer> { + private class MergeCalculator extends TaskDescriptor implements + Consumer, SVNException>> { private final static String ourOneShotStrategy = "svn.quickmerge.oneShotStrategy"; private final WCInfo myWcInfo; private final String mySourceUrl; private final String myBranchName; - private final AtomicReference> myCopyData; + private final + AtomicReference, SVNException>> + myCopyData; private boolean myIsReintegrate; private final List myNotMerged; private String myMergeTitle; private final MergeChecker myMergeChecker; - public void consume(SvnBranchPointsCalculator.WrapperInvertor branchCopyDataWrapperInvertor) { - myCopyData.set(branchCopyDataWrapperInvertor); + @Override + public void consume(TransparentlyFailedValueI, 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>(); + myCopyData = new AtomicReference, SVNException>>(); } //"Calculating not merged revisions" @Override public void run(ContinuationContext context) { - SvnBranchPointsCalculator.WrapperInvertor copyDataValue = myCopyData.get(); + SvnBranchPointsCalculator.WrapperInvertor 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; diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RunOrContinuation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RunOrContinuation.java index 4fd04b43cbef..d3fb3bf02da9 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RunOrContinuation.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/RunOrContinuation.java @@ -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 { +public abstract class RunOrContinuation { + private final static Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.dialogs.RunOrContinuation"); protected final Project myProject; private final String myTaskTitle; + private final Class myClazzE; private volatile boolean myWasCanceled; + private TransparentlyFailedValueI myTransparentlyFailedValue; - protected RunOrContinuation(final Project project, final String taskTitle) { + protected RunOrContinuation(final Project project, final String taskTitle, final Class clazzE) { myProject = project; myTaskTitle = taskTitle; + myClazzE = clazzE; + myTransparentlyFailedValue = new ThreadSafeTransparentlyFailedValue(); } @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); 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 refT = new AtomicReference(); - 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 { public void execute() { Continuation.createFragmented(myProject, true).run(getTask()); } - - /*@CalledInAwt - public void execute() { - final Ref refT = new Ref(); - 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()); - } - } - }); - } */ } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java index 1eddb0620979..2fd8b8b6dfa8 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/dialogs/SvnBranchPointsCalculator.java @@ -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> myCalculator; + private FactsCalculator, SVNException> myCalculator; private PersistentHolder myPersistentHolder; private File myFile; private final Project myProject; @@ -77,7 +77,8 @@ public class SvnBranchPointsCalculator { } }; - myCalculator = new FactsCalculator>(myProject, "Looking for branch origin", cache, new Loader(myProject)); + myCalculator = new FactsCalculator, 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> { + private static class Loader implements ThrowableConvertor, SVNException> { private SvnVcs myVcs; private Loader(final Project project) { myVcs = SvnVcs.getInstance(project); } - public WrapperInvertor convert(final KeyData keyData) { - final Ref> result = new Ref>(); + @Override + public WrapperInvertor convert(final KeyData keyData) throws SVNException { + final TransparentlyFailedValue consumer = new TransparentlyFailedValue(); + new FirstInBranch(myVcs, keyData.getRepoUrl(), keyData.getTargetUrl(), keyData.getSourceUrl(), consumer).run(); - new FirstInBranch(myVcs, keyData.getRepoUrl(), keyData.getTargetUrl(), keyData.getSourceUrl(), new Consumer() { - 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(! 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 invertor = result.get(); - if (LOG.isDebugEnabled()) { - LOG.debug("Loader17 returned: for key: " + keyData.toString() + " result: " + (invertor == null ? null : invertor.toString())); + WrapperInvertor invertor = new WrapperInvertor(! 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> consumer) { - myCalculator.get(new KeyData(repoUID, sourceUrl, targetUrl), consumer); - } - public TaskDescriptor getFirstCopyPointTask(final String repoUID, final String sourceUrl, final String targetUrl, - final Consumer> consumer) { - return myCalculator.getTask(new KeyData(repoUID, sourceUrl, targetUrl), consumer); - } - - public static abstract class CopyPointAcceptorTask extends TaskDescriptor implements Consumer> { - protected CopyPointAcceptorTask(String name, @NotNull Where where) { - super(name, where); - } + final Consumer, SVNException>> consumer) { + return myCalculator.getTask(new KeyData(repoUID, sourceUrl, targetUrl), consumer, SVNException.class); } } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java index 81c410f2c0fd..d19ed02f07c1 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/FirstInBranch.java @@ -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 myConsumer; + private final String myRepositoryRoot; + private final TransparentlyFailedValueI 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 consumer) { + public FirstInBranch(final SvnVcs vcs, final String repositoryRoot, final String branchUrl, final String trunkUrl, + final TransparentlyFailedValueI 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 exceptions = new HashSet(); + final boolean [] called = new boolean[1]; try { - branchURL = SVNURL.parseURIEncoded(myFullBranchUrl); - trunkURL = SVNURL.parseURIEncoded(myFullTrunkUrl); + createTask(SVNURL.parseURIDecoded(myRepositoryRoot), exceptions).consume(new Consumer() { + @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 tasks = - new ConcurrentTasks(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> createTask(final SVNURL branchURL) { + private Consumer> createTask(final SVNURL branchURL, final Set exceptions) { return new Consumer>() { public void consume(final Consumer copyDataConsumer) { final SVNLogClient logClient = ApplicationManager.getApplication().runReadAction(new Computable() { @@ -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 myRevisionCandidate = new Ref(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 myCopyDataConsumer; - myRevisionCandidate.set(logEntry.getRevision()); - } - }); + public MyLogEntryHandler(Consumer 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 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); + } } } 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 4b5c9040d08d..6000e30a1f0b 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnHistoryProvider.java @@ -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 { @@ -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 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 paths = logEntry.getChangedPaths(); - for (Map.Entry 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); } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnPathThroughHistoryCorrection.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnPathThroughHistoryCorrection.java new file mode 100644 index 000000000000..84e64910e6a8 --- /dev/null +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/history/SvnPathThroughHistoryCorrection.java @@ -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 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; + } +} diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java index 1b38358f99d8..724ce951200e 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/mergeinfo/SvnMergeInfoCache.java @@ -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() { - 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 result = new TransparentlyFailedValue() { + @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() { diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/ApplyPatchSaveToFileExecutor.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/ApplyPatchSaveToFileExecutor.java index 063d4b330093..72741cef4278 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/ApplyPatchSaveToFileExecutor.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/ApplyPatchSaveToFileExecutor.java @@ -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 patchGroups, LocalChangeList localList, String fileName, - TransparentlyFailedValue>, PatchSyntaxException> additionalInfo) { + TransparentlyFailedValueI>, PatchSyntaxException> additionalInfo) { final FileSaverDialog dialog = FileChooserFactory.getInstance().createSaveFileDialog( new FileSaverDescriptor("Save patch to", ""), myProject); final VirtualFile baseDir = myProject.getBaseDir(); diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java index 96f37f4bcc2a..2fd33334bf75 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/treeConflict/MergeFromTheirsResolver.java @@ -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 patchGroups, LocalChangeList localList, String fileName, - TransparentlyFailedValue>, PatchSyntaxException> additionalInfo) { + TransparentlyFailedValueI>, PatchSyntaxException> additionalInfo) { final List patches; try { patches = ApplyPatchSaveToFileExecutor.patchGroupsToOneGroup(patchGroups, myBaseDir);