/* * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.idea.svn.integrate; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.versionBrowser.ChangeBrowserSettings; import com.intellij.util.Consumer; import com.intellij.util.PairFunction; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.history.LogHierarchyNode; import org.jetbrains.idea.svn.history.SvnChangeList; import org.jetbrains.idea.svn.history.SvnCommittedChangesProvider; import org.jetbrains.idea.svn.history.SvnRepositoryLocation; import org.jetbrains.idea.svn.mergeinfo.MergeChecker; import org.jetbrains.idea.svn.mergeinfo.OneShotMergeInfoHelper; import java.util.List; import static com.intellij.openapi.progress.ProgressManager.progress; import static com.intellij.openapi.progress.ProgressManager.progress2; import static com.intellij.util.containers.ContainerUtil.newArrayList; import static java.lang.Math.min; import static org.jetbrains.idea.svn.SvnBundle.message; import static org.jetbrains.idea.svn.mergeinfo.SvnMergeInfoCache.MergeCheckResult; public class MergeCalculatorTask extends BaseMergeTask { public static final String PROP_BUNCH_SIZE = "idea.svn.quick.merge.bunch.size"; private final static int BUNCH_SIZE = 100; @Nullable private final SvnBranchPointsCalculator.WrapperInvertor myCopyPoint; @NotNull private final OneShotMergeInfoHelper myMergeChecker; @NotNull private final List<SvnChangeList> myChangeLists; @NotNull private final Consumer<MergeCalculatorTask> myCallback; private boolean myAllListsLoaded; public MergeCalculatorTask(@NotNull QuickMerge mergeProcess, @Nullable SvnBranchPointsCalculator.WrapperInvertor copyPoint, @NotNull Consumer<MergeCalculatorTask> callback) { super(mergeProcess); myCopyPoint = copyPoint; myCallback = callback; myChangeLists = newArrayList(); // TODO: Previously it was configurable - either to use OneShotMergeInfoHelper or BranchInfo as merge checker, but later that logic // TODO: was commented (in 80ebdbfea5210f6c998e67ddf28ca9c670fa4efe on 5/28/2010). // TODO: Still check if we need to preserve such configuration or it is sufficient to always use OneShotMergeInfoHelper. myMergeChecker = new OneShotMergeInfoHelper(myMergeContext); } public boolean areAllListsLoaded() { return myAllListsLoaded; } @NotNull public MergeChecker getMergeChecker() { return myMergeChecker; } @NotNull public List<SvnChangeList> getChangeLists() { return myChangeLists; } @Override public void run() throws VcsException { progress("Collecting merge information"); myMergeChecker.prepare(); if (myCopyPoint != null) { myChangeLists.addAll(getNotMergedChangeLists(getChangeListsAfter(myCopyPoint.getTrue().getTargetRevision()))); myAllListsLoaded = true; } else { Pair<List<SvnChangeList>, Boolean> loadResult = loadChangeLists(myMergeContext, -1, getBunchSize(-1)); myChangeLists.addAll(loadResult.first); myAllListsLoaded = loadResult.second; } if (!myChangeLists.isEmpty()) { myCallback.consume(this); } else { myMergeProcess.end("Everything is up-to-date", false); } } @NotNull private List<Pair<SvnChangeList, LogHierarchyNode>> getChangeListsAfter(long revision) throws VcsException { ChangeBrowserSettings settings = new ChangeBrowserSettings(); settings.CHANGE_AFTER = Long.toString(revision); settings.USE_CHANGE_AFTER_FILTER = true; return getChangeLists(myMergeContext, settings, revision, -1, Pair::create); } @NotNull private List<SvnChangeList> getNotMergedChangeLists(@NotNull List<Pair<SvnChangeList, LogHierarchyNode>> changeLists) { List<SvnChangeList> result = newArrayList(); progress("Collecting not merged revisions"); for (Pair<SvnChangeList, LogHierarchyNode> pair : changeLists) { SvnChangeList changeList = pair.getFirst(); progress2(message("progress.text2.processing.revision", changeList.getNumber())); if (MergeCheckResult.NOT_MERGED.equals(myMergeChecker.checkList(changeList)) && !myMergeChecker.checkListForPaths(pair.getSecond())) { result.add(changeList); } } return result; } @NotNull public static Pair<List<SvnChangeList>, Boolean> loadChangeLists(@NotNull MergeContext mergeContext, long beforeRevision, int size) throws VcsException { ChangeBrowserSettings settings = new ChangeBrowserSettings(); if (beforeRevision > 0) { settings.CHANGE_BEFORE = String.valueOf(beforeRevision); settings.USE_CHANGE_BEFORE_FILTER = true; } List<SvnChangeList> changeLists = getChangeLists(mergeContext, settings, beforeRevision, size, (changeList, tree) -> changeList); return Pair.create( changeLists.subList(0, min(size, changeLists.size())), changeLists.size() < size + 1); } public static int getBunchSize(int size) { Integer configuredSize = Integer.getInteger(PROP_BUNCH_SIZE); return configuredSize != null ? configuredSize : size > 0 ? size : BUNCH_SIZE; } @NotNull private static <T> List<T> getChangeLists(@NotNull MergeContext mergeContext, @NotNull ChangeBrowserSettings settings, long revisionToExclude, int size, @NotNull PairFunction<SvnChangeList, LogHierarchyNode, T> resultProvider) throws VcsException { List<T> result = newArrayList(); ((SvnCommittedChangesProvider)mergeContext.getVcs().getCommittedChangesProvider()) .getCommittedChangesWithMergedRevisons(settings, new SvnRepositoryLocation(mergeContext.getSourceUrl()), size > 0 ? size + (revisionToExclude > 0 ? 2 : 1) : 0, (changeList, tree) -> { if (revisionToExclude != changeList.getNumber()) { result.add(resultProvider.fun(changeList, tree)); } }); return result; } }