/* * 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.integrate; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.util.Consumer; import com.intellij.util.messages.Topic; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnBundle; import org.jetbrains.idea.svn.SvnConfiguration; import org.jetbrains.idea.svn.SvnVcs; import org.jetbrains.idea.svn.api.Depth; import org.jetbrains.idea.svn.update.UpdateEventHandler; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.SVNRevision; import org.tmatesoft.svn.core.wc.SVNRevisionRange; import org.tmatesoft.svn.core.wc2.SvnTarget; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class GroupMerger implements IMerger { protected final List myChangeLists; protected final File myTarget; private final SvnVcs myVcs; private final ProgressIndicator myProgressIndicator; protected final SVNURL myCurrentBranchUrl; private final StringBuilder myCommitMessage; protected final SvnConfiguration mySvnConfig; private final Project myProject; private UpdateEventHandler myHandler; private final String myBranchName; private final boolean myInverseRange; private final boolean myDryRun; private final Splitter mySplitter; private int myPackStart; private int myPackEnd; public GroupMerger(final SvnVcs vcs, final List changeLists, final File target, final UpdateEventHandler handler, final SVNURL currentBranchUrl, String branchName, boolean inverseRange, boolean dryRun, boolean stepByStep) { myBranchName = branchName; myInverseRange = inverseRange; myDryRun = dryRun; myProject = vcs.getProject(); mySvnConfig = SvnConfiguration.getInstance(vcs.getProject()); myCurrentBranchUrl = currentBranchUrl; myVcs = vcs; myChangeLists = changeLists; Collections.sort(myChangeLists, ByNumberChangeListComparator.getInstance()); mySplitter = stepByStep ? new StepByStepSplitter(myChangeLists) : new GroupSplitter(myChangeLists); myTarget = target; myProgressIndicator = ProgressManager.getInstance().getProgressIndicator(); myHandler = handler; myCommitMessage = new StringBuilder(); myPackStart = myPackEnd = -1; } public boolean hasNext() { return mySplitter.hasNext(); } public void mergeNext() throws SVNException, VcsException { final int step = mySplitter.step(); myPackStart = myPackEnd + 1; myPackEnd = myPackStart + step - 1; if (myProgressIndicator != null) { final String numbersBeingMerged; if ((myPackStart + 1) == myPackEnd) { numbersBeingMerged = "" + myChangeLists.get(myPackStart); } else { numbersBeingMerged = "" + myChangeLists.get(myPackStart) + "-" + myChangeLists.get(myPackEnd); } myProgressIndicator.setText2("Merging changelist(s)" + numbersBeingMerged); } doMerge(); appendComment(); } private void appendComment() { if (myCommitMessage.length() == 0) { myCommitMessage.append("Merged from ").append(myBranchName); } for (int i = myPackStart; i <= myPackEnd; i++) { final CommittedChangeList list = myChangeLists.get(i); final String nextComment = list.getComment().trim(); myCommitMessage.append('\n'); if (nextComment.length() > 0) { myCommitMessage.append(nextComment); } myCommitMessage.append(" [from revision ").append(list.getNumber()).append("]"); } } protected SVNRevisionRange createRange() { final SVNRevision start = SVNRevision.create(myChangeLists.get(myPackStart).getNumber() - 1); final SVNRevision end = SVNRevision.create(myChangeLists.get(myPackEnd).getNumber()); return myInverseRange ? new SVNRevisionRange(end, start) : new SVNRevisionRange(start, end); } protected void doMerge() throws VcsException { SvnTarget source = SvnTarget.fromURL(myCurrentBranchUrl); MergeClient client = myVcs.getFactory(myTarget).createMergeClient(); client.merge(source, createRange(), myTarget, Depth.INFINITY, mySvnConfig.isMergeDryRun(), myDryRun, true, mySvnConfig.getMergeOptions(), myHandler); } @NonNls private List getTail() { return (myPackStart < myChangeLists.size()) ? new ArrayList(myChangeLists.subList(myPackStart, myChangeLists.size())) : Collections.emptyList(); } public void getInfo(final Consumer holder, final boolean getLatest) { if (getLatest && (myPackStart != -1)) { final StringBuilder sb = new StringBuilder("Changelist(s) :"); for (int i = myPackStart; i <= myPackEnd; i++) { final CommittedChangeList list = myChangeLists.get(i); final String nextComment = list.getComment().trim().replace('\n', '|'); sb.append("\n" + list.getNumber()).append(" (").append(nextComment).append(")"); } sb.append(" merging faced problems"); holder.consume(sb.toString()); } getSkipped(holder); } public void getSkipped(final Consumer holder) { final List tail = getTail(); if (! tail.isEmpty()) { final StringBuilder sb = new StringBuilder(); for (int i = 0; i < tail.size(); i++) { CommittedChangeList list = tail.get(i); if (i != 0) { sb.append(','); } sb.append(list.getNumber()).append(" (").append(list.getComment().replace('\n', '|')).append(')'); } holder.consume(SvnBundle.message("action.Subversion.integrate.changes.warning.skipped.lists.text", sb.toString())); } } public String getComment() { return myCommitMessage.toString(); } @Nullable public File getMergeInfoHolder() { return myTarget; } public void afterProcessing() { // bounds approximately ok myProject.getMessageBus().syncPublisher(COMMITTED_CHANGES_MERGED_STATE).event(new ArrayList( myChangeLists.subList(0, myPackEnd > 0 ? myPackEnd : myChangeLists.size()))); } public static final Topic COMMITTED_CHANGES_MERGED_STATE = new Topic("COMMITTED_CHANGES_MERGED_STATE", CommittedChangesMergedStateChanged.class); public interface CommittedChangesMergedStateChanged { void event(final List list); } private static class StepByStepSplitter extends Splitter { private final int myTotal; private int myCnt; private StepByStepSplitter(final List lists) { super(lists); myTotal = lists.size(); myCnt = 0; } @Override public int step() { ++ myCnt; return 1; } @Override public boolean hasNext() { return myCnt < myTotal; } } }