blob: 73346ad4af66f3d99c02fc4a4934a62efc24539c [file] [log] [blame]
/*
* 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;
}
}
}