mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
svn tree conflicts: resolve "merge" if file-file and mine=move, theirs=edit
apply theirs text merges to new file location
This commit is contained in:
@@ -631,3 +631,5 @@ svn.option.ignore.whitespace.in.annotate=Ignore whitespace differences in annota
|
||||
annotation.show.merge.sources.default.text=Show merge source in annotations
|
||||
svn.cannot.save.credentials.store-auth-creds=Cannot store credentials: forbidden by "store-auth-creds = no"
|
||||
annotation.original.revision=Original revision
|
||||
confirmation.resolve.tree.conflict.merge.moved=Apply theirs changes to {0} moved to {1}?
|
||||
confirmation.resolve.tree.conflict.merge.renamed=Apply theirs changes to {0} renamed to {1}?
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.io.IOException;
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
class SvnContentRevision implements ContentRevision {
|
||||
public class SvnContentRevision implements ContentRevision {
|
||||
private final SvnVcs myVcs;
|
||||
protected final FilePath myFile;
|
||||
private final SVNRevision myRevision;
|
||||
|
||||
+154
-11
@@ -16,24 +16,36 @@
|
||||
package org.jetbrains.idea.svn.treeConflict;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diff.impl.patch.BinaryFilePatch;
|
||||
import com.intellij.openapi.diff.impl.patch.FilePatch;
|
||||
import com.intellij.openapi.diff.impl.patch.IdeaTextPatchBuilder;
|
||||
import com.intellij.openapi.diff.impl.patch.formove.PatchApplier;
|
||||
import com.intellij.openapi.progress.BackgroundTaskQueue;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vcs.AbstractVcsHelper;
|
||||
import com.intellij.openapi.vcs.FilePath;
|
||||
import com.intellij.openapi.vcs.FilePathImpl;
|
||||
import com.intellij.openapi.vcs.VcsException;
|
||||
import com.intellij.openapi.vcs.changes.*;
|
||||
import com.intellij.openapi.vcs.history.*;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.util.BeforeAfter;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.containers.Convertor;
|
||||
import com.intellij.util.continuation.*;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.util.ui.VcsBackgroundTask;
|
||||
import org.jetbrains.idea.svn.ConflictedSvnChange;
|
||||
import org.jetbrains.idea.svn.SvnRevisionNumber;
|
||||
import org.jetbrains.idea.svn.SvnVcs;
|
||||
import com.intellij.vcsUtil.VcsUtil;
|
||||
import gnu.trove.TLongArrayList;
|
||||
import org.jetbrains.idea.svn.*;
|
||||
import org.jetbrains.idea.svn.history.SvnFileRevision;
|
||||
import org.jetbrains.idea.svn.history.SvnHistoryProvider;
|
||||
import org.jetbrains.idea.svn.history.SvnHistorySession;
|
||||
import org.tmatesoft.svn.core.SVNException;
|
||||
@@ -46,6 +58,7 @@ import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -62,6 +75,7 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
|
||||
private SvnRevisionNumber myCommittedRevision;
|
||||
private FilePath myPath;
|
||||
private final List<Disposable> myChildDisposables;
|
||||
private final TLongArrayList myRightRevisionsList;
|
||||
|
||||
public TreeConflictRefreshablePanel(Project project, String loadingTitle, BackgroundTaskQueue queue, Change change) {
|
||||
super(project, loadingTitle, queue);
|
||||
@@ -70,6 +84,7 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
|
||||
myChange = (ConflictedSvnChange) change;
|
||||
myPath = ChangesUtil.getFilePath(myChange);
|
||||
myChildDisposables = new ArrayList<Disposable>();
|
||||
myRightRevisionsList = new TLongArrayList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,8 +133,7 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
|
||||
if (myChange.getBeforeRevision() != null) {
|
||||
myCommittedRevision = (SvnRevisionNumber)SvnHistorySession.getCurrentCommittedRevision(myVcs, myPath.getIOFile());
|
||||
}
|
||||
boolean differentURLs = description.getSourceLeftVersion() != null && description.getSourceRightVersion() != null &&
|
||||
! Comparing.equal(description.getSourceLeftVersion().getPath(), description.getSourceRightVersion().getPath());
|
||||
boolean differentURLs = isDifferentURLs(description);
|
||||
|
||||
ConflictSidePresentation leftSide = null;
|
||||
ConflictSidePresentation rightSide = null;
|
||||
@@ -161,6 +175,11 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
|
||||
return new BeforeAfter<ConflictSidePresentation>(leftSide, rightSide);
|
||||
}
|
||||
|
||||
private boolean isDifferentURLs(SVNTreeConflictDescription description) {
|
||||
return description.getSourceLeftVersion() != null && description.getSourceRightVersion() != null &&
|
||||
! Comparing.equal(description.getSourceLeftVersion().getPath(), description.getSourceRightVersion().getPath());
|
||||
}
|
||||
|
||||
private ConflictSidePresentation createSide(SVNConflictVersion version, final SVNRevision untilThisOther, final boolean isLeft) throws VcsException {
|
||||
if (version == null) return EmptyConflictSide.getInstance();
|
||||
SvnRevisionNumber number = null;
|
||||
@@ -176,7 +195,11 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
|
||||
} else {
|
||||
return new HistoryConflictSide(myVcs, version);
|
||||
}*/
|
||||
return new HistoryConflictSide(myVcs, version, untilThisOther);
|
||||
HistoryConflictSide side = new HistoryConflictSide(myVcs, version, untilThisOther);
|
||||
if (untilThisOther != null && ! isLeft) {
|
||||
side.setListToReportLoaded(myRightRevisionsList);
|
||||
}
|
||||
return side;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -230,8 +253,10 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
|
||||
enableAndSetListener(createMerge(description), merge);
|
||||
enableAndSetListener(createLeft(description), left);
|
||||
enableAndSetListener(createRight(description), right);
|
||||
/*wrapper.add(both);
|
||||
wrapper.add(merge);*/
|
||||
//wrapper.add(both);
|
||||
if (merge.isEnabled()) {
|
||||
wrapper.add(merge);
|
||||
}
|
||||
wrapper.add(left);
|
||||
wrapper.add(right);
|
||||
main.add(wrapper, gb);
|
||||
@@ -272,12 +297,118 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
|
||||
};
|
||||
}
|
||||
|
||||
private ActionListener createMerge(SVNTreeConflictDescription description) {
|
||||
return null; //To change body of created methods use File | Settings | File Templates.
|
||||
private ActionListener createMerge(final SVNTreeConflictDescription description) {
|
||||
if (isDifferentURLs(description)) {
|
||||
return null;
|
||||
}
|
||||
// my edit, theirs move or delete
|
||||
/*if (SVNConflictAction.DELETE.equals(description.getConflictAction()) && description.getSourceLeftVersion() != null) {
|
||||
//todo
|
||||
return new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
mergeMyEditTheirsDelete();
|
||||
}
|
||||
};
|
||||
} */
|
||||
if (SVNConflictAction.EDIT.equals(description.getConflictAction()) && description.getSourceLeftVersion() != null &&
|
||||
SVNConflictReason.DELETED.equals(description.getConflictReason()) && (myChange.isMoved() || myChange.isRenamed()) &&
|
||||
myCommittedRevision != null) {
|
||||
if (! myPath.isDirectory() && ! SVNNodeKind.DIR.equals(description.getSourceRightVersion().getKind())) {
|
||||
return createMergeTheirsForFile(description);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private ActionListener createMergeTheirsForFile(final SVNTreeConflictDescription description) {
|
||||
return new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final FilePath oldFilePath = myChange.getBeforeRevision().getFile();
|
||||
final FilePath newFilePath = myChange.getAfterRevision().getFile();
|
||||
int ok = Messages.showOkCancelDialog(myVcs.getProject(),
|
||||
(myChange.isMoved() ?
|
||||
SvnBundle.message("confirmation.resolve.tree.conflict.merge.moved", filePath(oldFilePath),
|
||||
filePath(newFilePath)) :
|
||||
SvnBundle.message("confirmation.resolve.tree.conflict.merge.renamed", filePath(oldFilePath),
|
||||
filePath(newFilePath))),
|
||||
"Resolve tree conflict", Messages.getQuestionIcon());
|
||||
if (Messages.OK != ok) return;
|
||||
|
||||
final String name = "Merge changes from theirs for: " + oldFilePath;
|
||||
|
||||
final GatheringContinuationContext cc = new GatheringContinuationContext();
|
||||
cc.addExceptionHandler(VcsException.class, new Consumer<VcsException>() {
|
||||
@Override
|
||||
public void consume(VcsException e) {
|
||||
AbstractVcsHelper.getInstance(myVcs.getProject()).showErrors(Collections.singletonList(e), name);
|
||||
}
|
||||
});
|
||||
cc.next(new TaskDescriptor("Creating patch for theirs changes", Where.POOLED) {
|
||||
@Override
|
||||
public void run(ContinuationContext context) {
|
||||
try {
|
||||
ProgressManager.progress("Getting base and theirs revisions content");
|
||||
final SvnContentRevision base =
|
||||
SvnContentRevision.createBaseRevision(myVcs, newFilePath, myCommittedRevision.getRevision());
|
||||
final SvnContentRevision remote =
|
||||
SvnContentRevision.createRemote(myVcs, oldFilePath,
|
||||
SVNRevision.create(description.getSourceRightVersion().getPegRevision()));
|
||||
final ContentRevision newBase =
|
||||
new SimpleContentRevision(base.getContent(), newFilePath, base.getRevisionNumber().asString());
|
||||
final ContentRevision newRemote =
|
||||
new SimpleContentRevision(remote.getContent(), newFilePath, remote.getRevisionNumber().asString());
|
||||
|
||||
ProgressManager.progress("Creating patch for theirs changes");
|
||||
final VirtualFile baseForPatch = ApplicationManager.getApplication().runReadAction(new Computable<VirtualFile>() {
|
||||
@Override
|
||||
public VirtualFile compute() {
|
||||
return ChangesUtil.findValidParent(newFilePath);
|
||||
}
|
||||
});
|
||||
final Project project = myVcs.getProject();
|
||||
final List<FilePatch> patches = IdeaTextPatchBuilder
|
||||
.buildPatch(project, Collections.singletonList(new Change(newBase, newRemote)), baseForPatch.getPath(), false);
|
||||
|
||||
ProgressManager.progress("Applying patch to " + newFilePath.getPath());
|
||||
final ChangeListManager clManager = ChangeListManager.getInstance(project);
|
||||
final LocalChangeList changeList = clManager.getChangeList(myChange);
|
||||
final PatchApplier<BinaryFilePatch> patchApplier =
|
||||
new PatchApplier<BinaryFilePatch>(project, baseForPatch, patches, changeList, null, null);
|
||||
patchApplier.scheduleSelf(false, context, true);
|
||||
context.last(new TaskDescriptor("Accepting working state", Where.POOLED) {
|
||||
@Override
|
||||
public void run(ContinuationContext context) {
|
||||
try {
|
||||
new SvnTreeConflictResolver(myVcs, oldFilePath, myCommittedRevision, null).resolveSelectMineFull(description);
|
||||
}
|
||||
catch (VcsException e1) {
|
||||
context.handleException(e1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (VcsException e1) {
|
||||
context.handleException(e1);
|
||||
}
|
||||
}
|
||||
});
|
||||
final Continuation fragmented = Continuation.createFragmented(myVcs.getProject(), false);
|
||||
fragmented.run(cc.getList());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private String filePath(FilePath newFilePath) {
|
||||
return newFilePath.getName() +
|
||||
" (" +
|
||||
newFilePath.getParentPath().getPath() +
|
||||
")";
|
||||
}
|
||||
|
||||
private ActionListener createBoth(SVNTreeConflictDescription description) {
|
||||
return null; //To change body of created methods use File | Settings | File Templates.
|
||||
return null;
|
||||
}
|
||||
|
||||
private void enableAndSetListener(final ActionListener al, final JButton b) {
|
||||
@@ -367,6 +498,7 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
|
||||
private final SvnVcs myVcs;
|
||||
private final SVNRevision myPeg;
|
||||
private FileHistoryPanelImpl myFileHistoryPanel;
|
||||
private TLongArrayList myListToReportLoaded;
|
||||
|
||||
private HistoryConflictSide(SvnVcs vcs, SVNConflictVersion version, final SVNRevision peg) throws VcsException {
|
||||
super(vcs.getProject(), version);
|
||||
@@ -386,6 +518,10 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
|
||||
myProvider = (SvnHistoryProvider) myVcs.getVcsHistoryProvider();
|
||||
}
|
||||
|
||||
public void setListToReportLoaded(TLongArrayList listToReportLoaded) {
|
||||
myListToReportLoaded = listToReportLoaded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VcsRevisionNumber convert(VcsFileRevision o) {
|
||||
return o.getRevisionNumber();
|
||||
@@ -400,6 +536,13 @@ public class TreeConflictRefreshablePanel extends AbstractRefreshablePanel {
|
||||
} else {
|
||||
myProvider.reportAppendableHistory(myPath, mySessionAdapter, from, myPeg, 0, myPeg, true);
|
||||
}
|
||||
VcsAbstractHistorySession session = mySessionAdapter.getSession();
|
||||
List<VcsFileRevision> list = session.getRevisionList();
|
||||
if (myListToReportLoaded != null) {
|
||||
for (VcsFileRevision revision : list) {
|
||||
myListToReportLoaded.add(((SvnRevisionNumber) revision.getRevisionNumber()).getRevision().getNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user