blob: ee4f807c257004273c674b54a48e66ab5b13121e [file] [log] [blame]
/*
* Copyright 2000-2009 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.actions;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.merge.MergeData;
import com.intellij.openapi.vcs.merge.MergeProvider;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.vcsUtil.VcsRunnable;
import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.svn.SvnPropertyKeys;
import org.jetbrains.idea.svn.SvnRevisionNumber;
import org.jetbrains.idea.svn.SvnUtil;
import org.jetbrains.idea.svn.SvnVcs;
import org.jetbrains.idea.svn.api.Depth;
import org.jetbrains.idea.svn.info.Info;
import org.jetbrains.idea.svn.properties.PropertyClient;
import org.jetbrains.idea.svn.properties.PropertyValue;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
/**
* @author lesya
* @author yole
*/
public class SvnMergeProvider implements MergeProvider {
private final Project myProject;
private static final Logger LOG = Logger.getInstance("#org.jetbrains.idea.svn.actions.SvnMergeProvider");
public SvnMergeProvider(final Project project) {
myProject = project;
}
@NotNull
public MergeData loadRevisions(final VirtualFile file) throws VcsException {
final MergeData data = new MergeData();
VcsRunnable runnable = new VcsRunnable() {
public void run() throws VcsException {
File oldFile = null;
File newFile = null;
File workingFile = null;
boolean mergeCase = false;
SvnVcs vcs = SvnVcs.getInstance(myProject);
Info info = vcs.getInfo(file);
if (info != null) {
oldFile = info.getConflictOldFile();
newFile = info.getConflictNewFile();
workingFile = info.getConflictWrkFile();
mergeCase = workingFile == null || workingFile.getName().contains("working");
// for debug
if (workingFile == null) {
LOG.info("Null working file when merging text conflict for " + file.getPath() + " old file: " + oldFile + " new file: " + newFile);
}
if (mergeCase) {
// this is merge case
oldFile = info.getConflictNewFile();
newFile = info.getConflictOldFile();
workingFile = info.getConflictWrkFile();
}
data.LAST_REVISION_NUMBER = new SvnRevisionNumber(info.getRevision());
} else {
throw new VcsException("Could not get info for " + file.getPath());
}
if (oldFile == null || newFile == null || workingFile == null) {
ByteArrayOutputStream bos = getBaseRevisionContents(vcs, file);
data.ORIGINAL = bos.toByteArray();
data.LAST = bos.toByteArray();
data.CURRENT = readFile(new File(file.getPath()));
}
else {
data.ORIGINAL = readFile(oldFile);
data.LAST = readFile(newFile);
data.CURRENT = readFile(workingFile);
}
if (mergeCase) {
final ByteArrayOutputStream contents = getBaseRevisionContents(vcs, file);
if (! Arrays.equals(contents.toByteArray(), data.ORIGINAL)) {
// swap base and server: another order of merge arguments
byte[] original = data.ORIGINAL;
data.ORIGINAL = data.LAST;
data.LAST = original;
}
}
}
};
VcsUtil.runVcsProcessWithProgress(runnable, VcsBundle.message("multiple.file.merge.loading.progress.title"), false, myProject);
return data;
}
private ByteArrayOutputStream getBaseRevisionContents(@NotNull SvnVcs vcs, @NotNull VirtualFile file) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
byte[] contents = SvnUtil.getFileContents(vcs, SvnTarget.fromFile(new File(file.getPath())), SVNRevision.BASE, SVNRevision.UNDEFINED);
bos.write(contents);
}
catch (VcsException e) {
LOG.warn(e);
}
catch (IOException e) {
LOG.warn(e);
}
return bos;
}
private static byte[] readFile(File workingFile) throws VcsException {
try {
return FileUtil.loadFileBytes(workingFile);
}
catch (IOException e) {
throw new VcsException(e);
}
}
public void conflictResolvedForFile(VirtualFile file) {
// TODO: Add possibility to resolve content conflicts separately from property conflicts.
SvnVcs vcs = SvnVcs.getInstance(myProject);
File path = new File(file.getPath());
try {
// TODO: Probably false should be passed to "resolveTree", but previous logic used true implicitly
vcs.getFactory(path).createConflictClient().resolve(path, Depth.EMPTY, false, true, true);
}
catch (VcsException e) {
LOG.warn(e);
}
// the .mine/.r## files have been deleted
final VirtualFile parent = file.getParent();
if (parent != null) {
parent.refresh(true, false);
}
}
public boolean isBinary(@NotNull final VirtualFile file) {
SvnVcs vcs = SvnVcs.getInstance(myProject);
try {
File ioFile = new File(file.getPath());
PropertyClient client = vcs.getFactory(ioFile).createPropertyClient();
PropertyValue value = client.getProperty(SvnTarget.fromFile(ioFile), SvnPropertyKeys.SVN_MIME_TYPE, false, SVNRevision.WORKING);
if (value != null && isBinaryMimeType(value.toString())) {
return true;
}
}
catch (VcsException e) {
LOG.warn(e);
}
return false;
}
private static boolean isBinaryMimeType(@NotNull String mimeType) {
return !mimeType.startsWith("text/");
}
}