| /* |
| * 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<CommittedChangeList> 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<CommittedChangeList> 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<CommittedChangeList> getTail() { |
| return (myPackStart < myChangeLists.size()) ? |
| new ArrayList<CommittedChangeList>(myChangeLists.subList(myPackStart, myChangeLists.size())) : |
| Collections.<CommittedChangeList>emptyList(); |
| } |
| |
| public void getInfo(final Consumer<String> 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<String> holder) { |
| final List<CommittedChangeList> 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<CommittedChangeList>( |
| myChangeLists.subList(0, myPackEnd > 0 ? myPackEnd : myChangeLists.size()))); |
| } |
| |
| public static final Topic<CommittedChangesMergedStateChanged> COMMITTED_CHANGES_MERGED_STATE = |
| new Topic<CommittedChangesMergedStateChanged>("COMMITTED_CHANGES_MERGED_STATE", CommittedChangesMergedStateChanged.class); |
| |
| public interface CommittedChangesMergedStateChanged { |
| void event(final List<CommittedChangeList> list); |
| } |
| |
| private static class StepByStepSplitter extends Splitter { |
| private final int myTotal; |
| private int myCnt; |
| |
| private StepByStepSplitter(final List<CommittedChangeList> lists) { |
| super(lists); |
| myTotal = lists.size(); |
| myCnt = 0; |
| } |
| |
| @Override |
| public int step() { |
| ++ myCnt; |
| return 1; |
| } |
| |
| @Override |
| public boolean hasNext() { |
| return myCnt < myTotal; |
| } |
| } |
| } |