mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
SVN: one-click merge: first ask whether to merge all; copy source persistent cache
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target({ElementType.METHOD})
|
||||
public @interface CalledInAny {
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.committed;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class RunBackgroundable {
|
||||
private RunBackgroundable() {
|
||||
}
|
||||
|
||||
public static void run(@NotNull final Task task) {
|
||||
final ProgressManager pm = ProgressManager.getInstance();
|
||||
if (ApplicationManager.getApplication().isDispatchThread()) {
|
||||
pm.run(task);
|
||||
} else {
|
||||
task.run(pm.getProgressIndicator());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@ import org.jetbrains.idea.svn.actions.ShowPropertiesDiffWithLocalAction;
|
||||
import org.jetbrains.idea.svn.actions.SvnMergeProvider;
|
||||
import org.jetbrains.idea.svn.annotate.SvnAnnotationProvider;
|
||||
import org.jetbrains.idea.svn.checkin.SvnCheckinEnvironment;
|
||||
import org.jetbrains.idea.svn.dialogs.SvnBranchPointsCalculator;
|
||||
import org.jetbrains.idea.svn.dialogs.SvnFormatWorker;
|
||||
import org.jetbrains.idea.svn.dialogs.WCInfo;
|
||||
import org.jetbrains.idea.svn.history.*;
|
||||
@@ -144,6 +145,8 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
|
||||
public static final Topic<Runnable> ROOTS_RELOADED = new Topic<Runnable>("ROOTS_RELOADED", Runnable.class);
|
||||
private VcsListener myVcsListener;
|
||||
|
||||
private SvnBranchPointsCalculator mySvnBranchPointsCalculator;
|
||||
|
||||
private final RootsToWorkingCopies myRootsToWorkingCopies;
|
||||
private final SvnAuthenticationNotifier myAuthNotifier;
|
||||
private static RareLogger.LogFilter[] ourLogFilters;
|
||||
@@ -365,6 +368,7 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
|
||||
FrameStateManager.getInstance().addListener(myFrameStateListener);
|
||||
|
||||
myAuthNotifier.init();
|
||||
mySvnBranchPointsCalculator = new SvnBranchPointsCalculator(myProject);
|
||||
|
||||
// do one time after project loaded
|
||||
StartupManager.getInstance(myProject).runWhenProjectIsInitialized(new DumbAwareRunnable() {
|
||||
@@ -466,6 +470,8 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
|
||||
|
||||
myAuthNotifier.stop();
|
||||
myAuthNotifier.clear();
|
||||
|
||||
mySvnBranchPointsCalculator = null;
|
||||
}
|
||||
|
||||
public VcsShowConfirmationOption getAddConfirmation() {
|
||||
@@ -1038,4 +1044,8 @@ public class SvnVcs extends AbstractVcs<CommittedChangeList> {
|
||||
public CommittedChangeList getRevisionChanges(VcsFileRevision revision, VirtualFile file) throws VcsException {
|
||||
return ShowAllSubmittedFilesAction.loadRevisions(getProject(), (SvnFileRevision)revision, file, false);
|
||||
}
|
||||
|
||||
public SvnBranchPointsCalculator getSvnBranchPointsCalculator() {
|
||||
return mySvnBranchPointsCalculator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class BranchMerger implements IMerger {
|
||||
}
|
||||
|
||||
public String getComment() {
|
||||
return "Merge from " + myBranchName;
|
||||
return "Merge all from " + myBranchName;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
|
||||
@@ -17,56 +17,29 @@ package org.jetbrains.idea.svn.dialogs;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.vcs.AbstractVcsHelper;
|
||||
import com.intellij.openapi.vcs.CommittedChangesProvider;
|
||||
import com.intellij.openapi.vcs.ObjectsConvertor;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesViewBalloonProblemNotifier;
|
||||
import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
|
||||
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.IdeFocusManager;
|
||||
import com.intellij.ui.DottedBorder;
|
||||
import com.intellij.ui.components.labels.LinkLabel;
|
||||
import com.intellij.ui.components.labels.LinkListener;
|
||||
import com.intellij.util.AsynchConsumer;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.intellij.util.io.EqualityPolicy;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.idea.svn.NestedCopyType;
|
||||
import org.jetbrains.idea.svn.SvnVcs;
|
||||
import org.jetbrains.idea.svn.WorkingCopyFormat;
|
||||
import org.jetbrains.idea.svn.actions.ChangeListsMergerFactory;
|
||||
import org.jetbrains.idea.svn.actions.SelectBranchPopup;
|
||||
import org.jetbrains.idea.svn.branchConfig.SvnBranchConfigurationNew;
|
||||
import org.jetbrains.idea.svn.checkout.SvnCheckoutProvider;
|
||||
import org.jetbrains.idea.svn.history.FirstInBranch;
|
||||
import org.jetbrains.idea.svn.history.SvnChangeList;
|
||||
import org.jetbrains.idea.svn.history.SvnRepositoryLocation;
|
||||
import org.jetbrains.idea.svn.integrate.IMerger;
|
||||
import org.jetbrains.idea.svn.integrate.MergerFactory;
|
||||
import org.jetbrains.idea.svn.integrate.SvnIntegrateChangesTask;
|
||||
import org.jetbrains.idea.svn.integrate.WorkingCopyInfo;
|
||||
import org.jetbrains.idea.svn.mergeinfo.BranchInfo;
|
||||
import org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache;
|
||||
import org.jetbrains.idea.svn.update.UpdateEventHandler;
|
||||
import org.tmatesoft.svn.core.SVNDepth;
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.SVNURL;
|
||||
import org.tmatesoft.svn.core.wc.SVNRevision;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -77,8 +50,6 @@ import java.awt.event.FocusEvent;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class CopiesPanel {
|
||||
@@ -291,149 +262,11 @@ public class CopiesPanel {
|
||||
private void mergeFrom(final WCInfo wcInfo, final VirtualFile root, final LinkLabel mergeLabel) {
|
||||
SelectBranchPopup.showForBranchRoot(myProject, root, new SelectBranchPopup.BranchSelectedCallback() {
|
||||
public void branchSelected(Project project, SvnBranchConfigurationNew configuration, String url, long revision) {
|
||||
if (url.equals(wcInfo.getRootUrl())) {
|
||||
showErrorBalloon("Cannot merge from self");
|
||||
return;
|
||||
}
|
||||
ProgressManager.getInstance().run(new MergeCalculator(wcInfo, root, url, configuration.getBaseName(url)));
|
||||
new QuickMerge(project, url, wcInfo, configuration.getBaseName(url)).execute();
|
||||
}
|
||||
}, "Select branch", mergeLabel);
|
||||
}
|
||||
|
||||
private void showErrorBalloon(final String s) {
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, s, MessageType.ERROR);
|
||||
}
|
||||
|
||||
private class MergeCalculator extends Task.Backgroundable {
|
||||
private final WCInfo myWcInfo;
|
||||
private final VirtualFile myRoot;
|
||||
private final String mySourceUrl;
|
||||
private final String myBranchName;
|
||||
private boolean myIsReintegrate;
|
||||
|
||||
private final List<CommittedChangeList> myNotMerged;
|
||||
private String myMergeTitle;
|
||||
private BranchInfo myBranchInfo;
|
||||
|
||||
private MergeCalculator(WCInfo wcInfo, VirtualFile root, String sourceUrl, String branchName) {
|
||||
super(CopiesPanel.this.myProject, "Calculating not merged revisions", true, BackgroundFromStartOption.getInstance());
|
||||
myWcInfo = wcInfo;
|
||||
myRoot = root;
|
||||
mySourceUrl = sourceUrl;
|
||||
myBranchName = branchName;
|
||||
myNotMerged = new LinkedList<CommittedChangeList>();
|
||||
myMergeTitle = "Merge from " + branchName;
|
||||
}
|
||||
|
||||
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
// branch is info holder
|
||||
new FirstInBranch(myVcs, myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl,
|
||||
new Consumer<FirstInBranch.CopyData>() {
|
||||
public void consume(FirstInBranch.CopyData copyData) {
|
||||
if (copyData == null) {
|
||||
showErrorBalloon("Merge start wasn't found");
|
||||
return;
|
||||
}
|
||||
|
||||
myIsReintegrate = ! copyData.isTrunkSupposedCorrect();
|
||||
if (! myWcInfo.getFormat().supportsMergeInfo()) return;
|
||||
final long localLatest = Math.max(copyData.getCopyTargetRevision(), copyData.getCopySourceRevision());
|
||||
myBranchInfo = new BranchInfo(myVcs, myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl, mySourceUrl, myVcs.createWCClient());
|
||||
|
||||
final CommittedChangesProvider<SvnChangeList,ChangeBrowserSettings> committedChangesProvider =
|
||||
myVcs.getCommittedChangesProvider();
|
||||
final ChangeBrowserSettings settings = new ChangeBrowserSettings();
|
||||
settings.CHANGE_AFTER = Long.toString(localLatest);
|
||||
try {
|
||||
committedChangesProvider.loadCommittedChanges(settings, new SvnRepositoryLocation(mySourceUrl),
|
||||
committedChangesProvider.getUnlimitedCountValue(), new AsynchConsumer<CommittedChangeList>() {
|
||||
public void finished() {
|
||||
}
|
||||
|
||||
public void consume(CommittedChangeList committedChangeList) {
|
||||
final SvnChangeList svnList = (SvnChangeList)committedChangeList;
|
||||
if (localLatest >= svnList.getNumber()) return;
|
||||
|
||||
final SvnMergeInfoCache.MergeCheckResult checkResult =
|
||||
myBranchInfo.checkList(svnList, myWcInfo.getPath());
|
||||
if (SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(checkResult)) {
|
||||
myNotMerged.add(svnList);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (VcsException e) {
|
||||
AbstractVcsHelper.getInstance(myProject).showErrors(Collections.singletonList(e), "Checking revisions for merge fault");
|
||||
}
|
||||
}
|
||||
}).run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
onSuccess();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MergerFactory askParameters() {
|
||||
final int result = Messages.showDialog(myProject, myNotMerged.size() + " not merged revision(s) found.", myMergeTitle,
|
||||
new String[]{"Merge &all", "&Select revisions to merge", "Cancel"}, 0, Messages.getQuestionIcon());
|
||||
if (result == 2) return null;
|
||||
|
||||
final MergerFactory factory;
|
||||
if (result == 0) {
|
||||
factory = createMergeAllFactory();
|
||||
} else {
|
||||
final ToBeMergedDialog dialog = new ToBeMergedDialog(myProject, myNotMerged, myMergeTitle, myBranchInfo);
|
||||
dialog.show();
|
||||
if (dialog.getExitCode() == DialogWrapper.CANCEL_EXIT_CODE) {
|
||||
return null;
|
||||
}
|
||||
final List<CommittedChangeList> lists = dialog.getSelected();
|
||||
if (lists.isEmpty()) return null;
|
||||
factory = new ChangeListsMergerFactory(lists);
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
private MergerFactory createMergeAllFactory() {
|
||||
return new MergerFactory() {
|
||||
public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl) {
|
||||
return new BranchMerger(vcs, currentBranchUrl, myWcInfo.getUrl(), myWcInfo.getPath(), handler, myIsReintegrate, myBranchName);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
if (! myWcInfo.getFormat().supportsMergeInfo()) {
|
||||
doMerge(createMergeAllFactory());
|
||||
return;
|
||||
}
|
||||
if (myNotMerged.isEmpty()) {
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, "Everything is up-to-date", MessageType.WARNING);
|
||||
return;
|
||||
}
|
||||
final MergerFactory factory = askParameters();
|
||||
if (factory == null) return;
|
||||
doMerge(factory);
|
||||
}
|
||||
|
||||
private void doMerge(MergerFactory factory) {
|
||||
final SVNURL sourceUrlUrl;
|
||||
try {
|
||||
sourceUrlUrl = SVNURL.parseURIEncoded(mySourceUrl);
|
||||
} catch (SVNException e) {
|
||||
showErrorBalloon("Cannot merge: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
final SvnIntegrateChangesTask task = new SvnIntegrateChangesTask(SvnVcs.getInstance(myProject),
|
||||
new WorkingCopyInfo(myWcInfo.getPath(), true), factory, sourceUrlUrl, myMergeTitle, false);
|
||||
ProgressManager.getInstance().run(task);
|
||||
}
|
||||
}
|
||||
|
||||
private void setFocusableForLinks(final LinkLabel label) {
|
||||
final Border border = new DottedBorder(new Insets(1,2,1,1), Color.black);
|
||||
label.setFocusable(true);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.dialogs;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vcs.CalledInAwt;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.ValueHolder;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
|
||||
// cache. persistent. by request
|
||||
public class FactsCalculator<In, Out> {
|
||||
private final Project myProject;
|
||||
private final String myTaskTitle;
|
||||
private final ValueHolder<Out, In> myCache;
|
||||
private final Convertor<In, Out> myLive;
|
||||
|
||||
public FactsCalculator(Project project, String taskTitle, ValueHolder<Out, In> cache, Convertor<In, Out> live) {
|
||||
myProject = project;
|
||||
myTaskTitle = taskTitle;
|
||||
myCache = cache;
|
||||
myLive = live;
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
public void get(final In in, final Consumer<Out> resultConsumer) {
|
||||
new RunOrContinuation<Out>(myProject, myTaskTitle) {
|
||||
@Override
|
||||
protected Out calculate() {
|
||||
return myCache.getValue(in);
|
||||
}
|
||||
@Override
|
||||
protected Out calculateLong() {
|
||||
final Out result = myLive.convert(in);
|
||||
if (result != null) {
|
||||
myCache.setValue(result, in);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
protected void processResult(Out out) {
|
||||
resultConsumer.consume(out);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.dialogs;
|
||||
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.vcs.*;
|
||||
import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
|
||||
import com.intellij.openapi.vcs.changes.committed.RunBackgroundable;
|
||||
import com.intellij.openapi.vcs.changes.ui.ChangesViewBalloonProblemNotifier;
|
||||
import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings;
|
||||
import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList;
|
||||
import com.intellij.util.AsynchConsumer;
|
||||
import com.intellij.util.Consumer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.idea.svn.SvnVcs;
|
||||
import org.jetbrains.idea.svn.actions.ChangeListsMergerFactory;
|
||||
import org.jetbrains.idea.svn.history.CopyData;
|
||||
import org.jetbrains.idea.svn.history.FirstInBranch;
|
||||
import org.jetbrains.idea.svn.history.SvnChangeList;
|
||||
import org.jetbrains.idea.svn.history.SvnRepositoryLocation;
|
||||
import org.jetbrains.idea.svn.integrate.IMerger;
|
||||
import org.jetbrains.idea.svn.integrate.MergerFactory;
|
||||
import org.jetbrains.idea.svn.integrate.SvnIntegrateChangesTask;
|
||||
import org.jetbrains.idea.svn.integrate.WorkingCopyInfo;
|
||||
import org.jetbrains.idea.svn.mergeinfo.BranchInfo;
|
||||
import org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache;
|
||||
import org.jetbrains.idea.svn.update.UpdateEventHandler;
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
import org.tmatesoft.svn.core.SVNURL;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class QuickMerge {
|
||||
private final Project myProject;
|
||||
private final String myBranchName;
|
||||
private final WCInfo myWcInfo;
|
||||
private final String mySourceUrl;
|
||||
private SvnVcs myVcs;
|
||||
|
||||
public QuickMerge(Project project, String sourceUrl, WCInfo wcInfo, final String branchName) {
|
||||
myProject = project;
|
||||
myBranchName = branchName;
|
||||
myVcs = SvnVcs.getInstance(project);
|
||||
mySourceUrl = sourceUrl;
|
||||
myWcInfo = wcInfo;
|
||||
}
|
||||
|
||||
@CalledInAwt
|
||||
public void execute() {
|
||||
if (mySourceUrl.equals(myWcInfo.getRootUrl())) {
|
||||
showErrorBalloon("Cannot merge from self");
|
||||
return;
|
||||
}
|
||||
|
||||
if (! myWcInfo.getFormat().supportsMergeInfo()) {
|
||||
mergeAll();
|
||||
return;
|
||||
}
|
||||
|
||||
final int result = Messages.showDialog(myProject, "Merge all?", "Merge from " + myBranchName,
|
||||
new String[]{"Merge &all", "&Select revisions to merge", "Cancel"}, 0, Messages.getQuestionIcon());
|
||||
if (result == 2) return;
|
||||
if (result == 0) {
|
||||
mergeAll();
|
||||
return;
|
||||
}
|
||||
|
||||
ProgressManager.getInstance().run(new MergeCalculator(myProject, myWcInfo, mySourceUrl, myBranchName));
|
||||
}
|
||||
|
||||
@CalledInAny
|
||||
private void showErrorBalloon(final String s) {
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, s, MessageType.ERROR);
|
||||
}
|
||||
|
||||
// continuation... continuation.. hidden continuation..
|
||||
private void mergeAll() {
|
||||
// suppose we're in branch
|
||||
myVcs.getSvnBranchPointsCalculator().getFirstCopyPoint(myWcInfo.getRepositoryRoot(), mySourceUrl, myWcInfo.getRootUrl(),
|
||||
new Consumer<SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData>>() {
|
||||
public void consume(SvnBranchPointsCalculator.WrapperInvertor<SvnBranchPointsCalculator.BranchCopyData> result) {
|
||||
if (result == null) {
|
||||
showErrorBalloon("Merge start wasn't found");
|
||||
return;
|
||||
}
|
||||
final boolean reintegrate = result.isInvertedSense();
|
||||
final MergerFactory mergerFactory = new MergerFactory() {
|
||||
public IMerger createMerger(SvnVcs vcs, File target, UpdateEventHandler handler, SVNURL currentBranchUrl) {
|
||||
return new BranchMerger(vcs, currentBranchUrl, myWcInfo.getUrl(), myWcInfo.getPath(), handler, reintegrate, myBranchName);
|
||||
}
|
||||
};
|
||||
|
||||
final String title = "Merging all from " + myBranchName + (reintegrate ? " (reintegrate)" : "");
|
||||
doMerge(mergerFactory, title);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@CalledInAny
|
||||
private void doMerge(final MergerFactory factory, final String mergeTitle) {
|
||||
final SVNURL sourceUrlUrl;
|
||||
try {
|
||||
sourceUrlUrl = SVNURL.parseURIEncoded(mySourceUrl);
|
||||
} catch (SVNException e) {
|
||||
showErrorBalloon("Cannot merge: " + e.getMessage());
|
||||
return;
|
||||
}
|
||||
final SvnIntegrateChangesTask task = new SvnIntegrateChangesTask(SvnVcs.getInstance(myProject),
|
||||
new WorkingCopyInfo(myWcInfo.getPath(), true), factory, sourceUrlUrl, mergeTitle, false);
|
||||
RunBackgroundable.run(task);
|
||||
}
|
||||
|
||||
private class MergeCalculator extends Task.Backgroundable {
|
||||
private final WCInfo myWcInfo;
|
||||
private final String mySourceUrl;
|
||||
private final String myBranchName;
|
||||
private boolean myIsReintegrate;
|
||||
|
||||
private final List<CommittedChangeList> myNotMerged;
|
||||
private String myMergeTitle;
|
||||
private BranchInfo myBranchInfo;
|
||||
|
||||
private MergeCalculator(Project project, WCInfo wcInfo, String sourceUrl, String branchName) {
|
||||
super(project, "Calculating not merged revisions", true, BackgroundFromStartOption.getInstance());
|
||||
myWcInfo = wcInfo;
|
||||
mySourceUrl = sourceUrl;
|
||||
myBranchName = branchName;
|
||||
myNotMerged = new LinkedList<CommittedChangeList>();
|
||||
myMergeTitle = "Merge from " + branchName;
|
||||
}
|
||||
|
||||
public void run(@NotNull ProgressIndicator indicator) {
|
||||
// branch is info holder
|
||||
new FirstInBranch(myVcs, myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl,
|
||||
new Consumer<CopyData>() {
|
||||
public void consume(CopyData copyData) {
|
||||
if (copyData == null) {
|
||||
showErrorBalloon("Merge start wasn't found");
|
||||
return;
|
||||
}
|
||||
|
||||
myIsReintegrate = ! copyData.isTrunkSupposedCorrect();
|
||||
if (! myWcInfo.getFormat().supportsMergeInfo()) return;
|
||||
final long localLatest = Math.max(copyData.getCopyTargetRevision(), copyData.getCopySourceRevision());
|
||||
myBranchInfo = new BranchInfo(myVcs, myWcInfo.getRepositoryRoot(), myWcInfo.getRootUrl(), mySourceUrl, mySourceUrl, myVcs.createWCClient());
|
||||
|
||||
final CommittedChangesProvider<SvnChangeList,ChangeBrowserSettings> committedChangesProvider =
|
||||
myVcs.getCommittedChangesProvider();
|
||||
final ChangeBrowserSettings settings = new ChangeBrowserSettings();
|
||||
settings.CHANGE_AFTER = Long.toString(localLatest);
|
||||
try {
|
||||
committedChangesProvider.loadCommittedChanges(settings, new SvnRepositoryLocation(mySourceUrl),
|
||||
committedChangesProvider.getUnlimitedCountValue(), new AsynchConsumer<CommittedChangeList>() {
|
||||
public void finished() {
|
||||
}
|
||||
|
||||
public void consume(CommittedChangeList committedChangeList) {
|
||||
final SvnChangeList svnList = (SvnChangeList)committedChangeList;
|
||||
if (localLatest >= svnList.getNumber()) return;
|
||||
|
||||
final SvnMergeInfoCache.MergeCheckResult checkResult =
|
||||
myBranchInfo.checkList(svnList, myWcInfo.getPath());
|
||||
if (SvnMergeInfoCache.MergeCheckResult.NOT_MERGED.equals(checkResult)) {
|
||||
myNotMerged.add(svnList);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (VcsException e) {
|
||||
AbstractVcsHelper.getInstance(myProject).showErrors(Collections.singletonList(e), "Checking revisions for merge fault");
|
||||
}
|
||||
}
|
||||
}).run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
onSuccess();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private MergerFactory askParameters() {
|
||||
final MergerFactory factory;
|
||||
final ToBeMergedDialog dialog = new ToBeMergedDialog(myProject, myNotMerged, myMergeTitle, myBranchInfo);
|
||||
dialog.show();
|
||||
if (dialog.getExitCode() == DialogWrapper.CANCEL_EXIT_CODE) {
|
||||
return null;
|
||||
}
|
||||
final List<CommittedChangeList> lists = dialog.getSelected();
|
||||
if (lists.isEmpty()) return null;
|
||||
factory = new ChangeListsMergerFactory(lists);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
if (myNotMerged.isEmpty()) {
|
||||
ChangesViewBalloonProblemNotifier.showMe(myProject, "Everything is up-to-date", MessageType.WARNING);
|
||||
return;
|
||||
}
|
||||
final MergerFactory factory = askParameters();
|
||||
if (factory == null) return;
|
||||
doMerge(factory, myMergeTitle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.dialogs;
|
||||
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.vcs.CalledInAwt;
|
||||
import com.intellij.openapi.vcs.CalledInBackground;
|
||||
import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class RunOrContinuation<T> {
|
||||
protected final Project myProject;
|
||||
private final String myTaskTitle;
|
||||
private boolean myWasCanceled;
|
||||
|
||||
protected RunOrContinuation(final Project project, final String taskTitle) {
|
||||
myProject = project;
|
||||
myTaskTitle = taskTitle;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@CalledInAwt
|
||||
protected abstract T calculate();
|
||||
@Nullable
|
||||
@CalledInBackground
|
||||
protected abstract T calculateLong();
|
||||
@CalledInAwt
|
||||
protected abstract void processResult(final T t);
|
||||
|
||||
protected void cancel() {
|
||||
myWasCanceled = true;
|
||||
}
|
||||
|
||||
@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) && (! refT.isNull())) processResult(refT.get());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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.dialogs;
|
||||
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.ValueHolder;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.io.DataExternalizer;
|
||||
import com.intellij.util.io.EnumeratorStringDescriptor;
|
||||
import com.intellij.util.io.PersistentHashMap;
|
||||
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 java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
public class SvnBranchPointsCalculator {
|
||||
private final FactsCalculator<KeyData, WrapperInvertor<BranchCopyData>> myCalculator;
|
||||
|
||||
private PersistentHolder myPersistentHolder;
|
||||
|
||||
public SvnBranchPointsCalculator(final Project project) {
|
||||
final File vcs = new File(PathManager.getSystemPath(), "vcs");
|
||||
File file = new File(vcs, "svn_copy_sources");
|
||||
file.mkdirs();
|
||||
file = new File(file, project.getLocationHash());
|
||||
|
||||
// todo when will it die?
|
||||
ValueHolder<WrapperInvertor<BranchCopyData>, KeyData> cache = null;
|
||||
|
||||
try {
|
||||
myPersistentHolder = new PersistentHolder(file);
|
||||
cache = new ValueHolder<WrapperInvertor<BranchCopyData>, KeyData>() {
|
||||
public WrapperInvertor<BranchCopyData> getValue(KeyData dataHolder) {
|
||||
try {
|
||||
return myPersistentHolder.getBestHit(dataHolder.getRepoUrl(), dataHolder.getSourceUrl(), dataHolder.getTargetUrl());
|
||||
} catch (IOException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// todo carefully check
|
||||
public void setValue(WrapperInvertor<BranchCopyData> value, KeyData dataHolder) {
|
||||
try {
|
||||
myPersistentHolder.put(dataHolder.getRepoUrl(),
|
||||
value.isInvertedSense() ? dataHolder.getSourceUrl() : dataHolder.getTargetUrl(), value.getWrapped());
|
||||
}
|
||||
catch (IOException e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (IOException e) {
|
||||
cache = new ValueHolder<WrapperInvertor<BranchCopyData>, KeyData>() {
|
||||
public WrapperInvertor<BranchCopyData> getValue(KeyData dataHolder) {
|
||||
return null;
|
||||
}
|
||||
public void setValue(WrapperInvertor<BranchCopyData> value, KeyData dataHolder) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
myCalculator = new FactsCalculator<KeyData, WrapperInvertor<BranchCopyData>>(project, "Looking for branch origin", cache, new Loader(project));
|
||||
}
|
||||
|
||||
private static class BranchDataExternalizer implements DataExternalizer<TreeMap<String,BranchCopyData>> {
|
||||
public void save(DataOutput out, TreeMap<String,BranchCopyData> value) throws IOException {
|
||||
out.writeInt(value.size());
|
||||
for (Map.Entry<String, BranchCopyData> entry : value.entrySet()) {
|
||||
out.writeUTF(entry.getKey());
|
||||
final BranchCopyData entryValue = entry.getValue();
|
||||
out.writeUTF(entryValue.getSource());
|
||||
out.writeUTF(entryValue.getTarget());
|
||||
out.writeLong(entryValue.getSourceRevision());
|
||||
out.writeLong(entryValue.getTargetRevision());
|
||||
}
|
||||
}
|
||||
|
||||
public TreeMap<String,BranchCopyData> read(DataInput in) throws IOException {
|
||||
final TreeMap<String,BranchCopyData> result = new TreeMap<String, BranchCopyData>();
|
||||
|
||||
final int num = in.readInt();
|
||||
for (int i = 0; i < num; i++) {
|
||||
final String key = in.readUTF();
|
||||
final String source = in.readUTF();
|
||||
final String target = in.readUTF();
|
||||
final long sourceRevision = in.readLong();
|
||||
final long targetRevision = in.readLong();
|
||||
|
||||
result.put(key, new BranchCopyData(source, sourceRevision, target, targetRevision));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public static class WrapperInvertor<T extends Invertor<T>> {
|
||||
private final T myWrapped;
|
||||
private final boolean myInvertedSense;
|
||||
|
||||
public WrapperInvertor(boolean invertedSense, T wrapped) {
|
||||
myInvertedSense = invertedSense;
|
||||
myWrapped = wrapped;
|
||||
}
|
||||
|
||||
public boolean isInvertedSense() {
|
||||
return myInvertedSense;
|
||||
}
|
||||
|
||||
public T getWrapped() {
|
||||
return myWrapped;
|
||||
}
|
||||
|
||||
public T getTrue() {
|
||||
return myInvertedSense ? myWrapped.invertSelf() : myWrapped;
|
||||
}
|
||||
|
||||
public T inverted() {
|
||||
return myWrapped.invertSelf();
|
||||
}
|
||||
}
|
||||
|
||||
private static class PersistentHolder {
|
||||
private final PersistentHashMap<String, TreeMap<String, BranchCopyData>> myPersistentMap;
|
||||
private final MultiMap<String, String> myForSearchMap;
|
||||
private final Object myLock;
|
||||
|
||||
PersistentHolder(final File file) throws IOException {
|
||||
myLock = new Object();
|
||||
myPersistentMap = new PersistentHashMap<String, TreeMap<String, BranchCopyData>>(
|
||||
file, new EnumeratorStringDescriptor(), new BranchDataExternalizer());
|
||||
final Ref<IOException> excRef = new Ref<IOException>();
|
||||
// list for values by default
|
||||
myForSearchMap = new MultiMap<String, String>();
|
||||
myPersistentMap.iterateData(new Processor<String>() {
|
||||
public boolean process(final String s) {
|
||||
try {
|
||||
final TreeMap<String, BranchCopyData> map = myPersistentMap.get(s);
|
||||
myForSearchMap.put(s, new ArrayList<String>(map.keySet()));
|
||||
}
|
||||
catch (IOException e) {
|
||||
excRef.set(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (! excRef.isNull()) {
|
||||
throw excRef.get();
|
||||
}
|
||||
}
|
||||
|
||||
public void put(final String uid, final String target, final BranchCopyData data) throws IOException {
|
||||
// todo - rewrite of rather big piece; consider rewriting
|
||||
synchronized (myLock) {
|
||||
TreeMap<String, BranchCopyData> map = myPersistentMap.get(uid);
|
||||
if (map == null) {
|
||||
map = new TreeMap<String, BranchCopyData>();
|
||||
}
|
||||
map.put(target, data);
|
||||
myPersistentMap.put(uid, map);
|
||||
myForSearchMap.putValue(uid, target);
|
||||
}
|
||||
myPersistentMap.force();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WrapperInvertor<BranchCopyData> getBestHit(final String repoUrl, final String source, final String target) throws IOException {
|
||||
final List<String> keys;
|
||||
synchronized (myLock) {
|
||||
keys = (List<String>) myForSearchMap.get(repoUrl);
|
||||
}
|
||||
// keys are never removed, so we can use 2 synchronized blocks
|
||||
final String sourceMatching = getMatchingUrl(keys, source);
|
||||
final String targetMatching = getMatchingUrl(keys, target);
|
||||
|
||||
if (sourceMatching == null && targetMatching == null) return null;
|
||||
|
||||
synchronized (myLock) {
|
||||
final TreeMap<String, BranchCopyData> map = myPersistentMap.get(repoUrl);
|
||||
|
||||
final boolean sourceIsOut = sourceMatching == null;
|
||||
if (sourceIsOut || targetMatching == null) {
|
||||
// if found by "target" url - we correctly thought that target of copy is target
|
||||
return sourceIsOut ? new WrapperInvertor<BranchCopyData>(false, map.get(targetMatching)) :
|
||||
new WrapperInvertor<BranchCopyData>(false, map.get(sourceMatching));
|
||||
}
|
||||
final BranchCopyData sourceData = map.get(sourceMatching);
|
||||
final BranchCopyData targetData = map.get(targetMatching);
|
||||
|
||||
final boolean inverted = sourceData.getTargetRevision() > targetData.getTargetRevision();
|
||||
return new WrapperInvertor<BranchCopyData>(inverted, inverted ? sourceData : targetData);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getMatchingUrl(List<String> keys, String source) {
|
||||
final int idx = Collections.binarySearch(keys, source);
|
||||
if (idx >= 0) return keys.get(idx);
|
||||
final int beforeInsertionIdx = - idx - 2;
|
||||
if (beforeInsertionIdx < 0) return null;
|
||||
final String candidate = keys.get(beforeInsertionIdx);
|
||||
if (source.startsWith(candidate)) return candidate;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Loader implements Convertor<KeyData, WrapperInvertor<BranchCopyData>> {
|
||||
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>>();
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}).run();
|
||||
return result.get();
|
||||
}
|
||||
}
|
||||
|
||||
private static class KeyData {
|
||||
private final String myRepoUrl;
|
||||
private final String mySourceUrl;
|
||||
private final String myTargetUrl;
|
||||
|
||||
public KeyData(final String repoUID, final String sourceUrl, final String targetUrl) {
|
||||
myRepoUrl = repoUID;
|
||||
mySourceUrl = sourceUrl;
|
||||
myTargetUrl = targetUrl;
|
||||
}
|
||||
|
||||
public String getRepoUrl() {
|
||||
return myRepoUrl;
|
||||
}
|
||||
|
||||
public String getSourceUrl() {
|
||||
return mySourceUrl;
|
||||
}
|
||||
|
||||
public String getTargetUrl() {
|
||||
return myTargetUrl;
|
||||
}
|
||||
}
|
||||
|
||||
public static class BranchCopyData implements Invertor<BranchCopyData> {
|
||||
private final String mySource;
|
||||
private final String myTarget;
|
||||
private final long mySourceRevision;
|
||||
private final long myTargetRevision;
|
||||
|
||||
public BranchCopyData(String source, long sourceRevision, String target, long targetRevision) {
|
||||
mySource = source;
|
||||
mySourceRevision = sourceRevision;
|
||||
myTarget = target;
|
||||
myTargetRevision = targetRevision;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return mySource;
|
||||
}
|
||||
|
||||
public long getSourceRevision() {
|
||||
return mySourceRevision;
|
||||
}
|
||||
|
||||
public String getTarget() {
|
||||
return myTarget;
|
||||
}
|
||||
|
||||
public long getTargetRevision() {
|
||||
return myTargetRevision;
|
||||
}
|
||||
|
||||
public BranchCopyData invertSelf() {
|
||||
return new BranchCopyData(myTarget, myTargetRevision, mySource, mySourceRevision);
|
||||
}
|
||||
}
|
||||
|
||||
private interface Invertor<T> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2000-2010 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;
|
||||
|
||||
public class CopyData {
|
||||
private final long myCopySourceRevision;
|
||||
private final long myCopyTargetRevision;
|
||||
private final boolean myTrunkSupposedCorrect;
|
||||
|
||||
public CopyData(long copySourceRevision, long copyTargetRevision, boolean trunkSupposedCorrect) {
|
||||
myCopySourceRevision = copySourceRevision;
|
||||
myCopyTargetRevision = copyTargetRevision;
|
||||
myTrunkSupposedCorrect = trunkSupposedCorrect;
|
||||
}
|
||||
|
||||
public long getCopySourceRevision() {
|
||||
return myCopySourceRevision;
|
||||
}
|
||||
|
||||
public long getCopyTargetRevision() {
|
||||
return myCopyTargetRevision;
|
||||
}
|
||||
|
||||
public boolean isTrunkSupposedCorrect() {
|
||||
return myTrunkSupposedCorrect;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.jetbrains.idea.svn.history;
|
||||
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.util.Consumer;
|
||||
import org.jetbrains.idea.svn.SvnVcs;
|
||||
import org.tmatesoft.svn.core.*;
|
||||
@@ -65,6 +66,8 @@ public class FirstInBranch implements Runnable {
|
||||
logClient.doLog(SVNURL.parseURIEncoded(myFullBranchUrl), null, SVNRevision.UNDEFINED, SVNRevision.HEAD, SVNRevision.create(0), true, true, 0,
|
||||
new ISVNLogEntryHandler() {
|
||||
public void handleLogEntry(final SVNLogEntry logEntry) throws SVNException {
|
||||
ProgressManager.checkCanceled();
|
||||
|
||||
final Map map = logEntry.getChangedPaths();
|
||||
for (Object o : map.values()) {
|
||||
final SVNLogEntryPath path = (SVNLogEntryPath) o;
|
||||
@@ -97,27 +100,4 @@ public class FirstInBranch implements Runnable {
|
||||
|
||||
private static class MockException extends RuntimeException {}
|
||||
|
||||
public static class CopyData {
|
||||
private final long myCopySourceRevision;
|
||||
private final long myCopyTargetRevision;
|
||||
private final boolean myTrunkSupposedCorrect;
|
||||
|
||||
public CopyData(long copySourceRevision, long copyTargetRevision, boolean trunkSupposedCorrect) {
|
||||
myCopySourceRevision = copySourceRevision;
|
||||
myCopyTargetRevision = copyTargetRevision;
|
||||
myTrunkSupposedCorrect = trunkSupposedCorrect;
|
||||
}
|
||||
|
||||
public long getCopySourceRevision() {
|
||||
return myCopySourceRevision;
|
||||
}
|
||||
|
||||
public long getCopyTargetRevision() {
|
||||
return myCopyTargetRevision;
|
||||
}
|
||||
|
||||
public boolean isTrunkSupposedCorrect() {
|
||||
return myTrunkSupposedCorrect;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.idea.svn.SvnVcs;
|
||||
import org.jetbrains.idea.svn.dialogs.WCInfoWithBranches;
|
||||
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.internal.wc.DefaultSVNOptions;
|
||||
@@ -167,8 +168,8 @@ public class SvnMergeInfoCache {
|
||||
myRevision = -1;
|
||||
|
||||
ApplicationManager.getApplication().executeOnPooledThread(new FirstInBranch(vcs, repositoryRoot, branchUrl, trunkUrl,
|
||||
new Consumer<FirstInBranch.CopyData>() {
|
||||
public void consume(FirstInBranch.CopyData copyData) {
|
||||
new Consumer<CopyData>() {
|
||||
public void consume(CopyData copyData) {
|
||||
if (copyData == null) return;
|
||||
myRevision = copyData.getCopySourceRevision();
|
||||
if (myRevision != -1) {
|
||||
|
||||
Reference in New Issue
Block a user