+++ /dev/null
-#!/usr/bin/env python
-
-from mercurial import filemerge, ui, util
-from mercurial.node import short
-import sys, struct, socket
-
-def monkeypatch_method(cls):
- def decorator(func):
- setattr(cls, func.__name__, func)
- return func
- return decorator
-
-@monkeypatch_method(filemerge)
-def filemerge(repo, mynode, orig, fcd, fco, fca):
- port = int(repo.ui.config( 'hg4ideafilemerge', 'port', None, True))
-
- repo.ui.debug( "hg4idea server waiting on port %s" % port )
-
- if not port:
- util.abort("No port was specified")
-
- def send( client, data ):
- length = struct.pack('>L', len(data))
- client.sendall( length )
- client.sendall( data)
-
- client = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
- repo.ui.debug( "connecting ..." )
- try:
- client.connect( ('127.0.0.1', port) )
- repo.ui.debug( "connected, sending data ..." )
- send( client, fcd.data() )
- send( client, fco.data() )
- send( client, fca.data() )
- print client.recv(1024)
- return 1;
- except:
- util.abort( "Could not send data to hg4idea")
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FilePath;
-import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vfs.VirtualFile;
import org.apache.commons.lang.StringUtils;
import org.zmlx.hg4idea.HgFile;
-import org.zmlx.hg4idea.HgUtil;
-import java.io.*;
-import java.net.Socket;
-import java.util.*;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import static org.zmlx.hg4idea.HgErrorHandler.ensureSuccess;
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
public class HgResolveCommand {
- private static File FILEMERGE_PLUGIN;
-
private static final int ITEM_COUNT = 3;
- private final Project project;
+ private final Project myProject;
public HgResolveCommand(Project project) {
- this.project = project;
- if (FILEMERGE_PLUGIN == null) {
- FILEMERGE_PLUGIN = HgUtil.getTemporaryPythonFile("filemerge");
- }
+ myProject = project;
}
public Map<HgFile, HgResolveStatusEnum> list(VirtualFile repo) {
if (repo == null) {
return Collections.emptyMap();
}
+ final HgCommandResult result = HgCommandService.getInstance(myProject).execute(repo, "resolve", Arrays.asList("--list"));
+ if (result == null) {
+ return Collections.emptyMap();
+ }
- HgCommandResult result = HgCommandService.getInstance(project)
- .execute(repo, "resolve", Arrays.asList("--list"));
-
- Map<HgFile, HgResolveStatusEnum> resolveStatus = new HashMap<HgFile, HgResolveStatusEnum>();
+ final Map<HgFile, HgResolveStatusEnum> resolveStatus = new HashMap<HgFile, HgResolveStatusEnum>();
for (String line : result.getOutputLines()) {
if (StringUtils.isBlank(line) || line.length() < ITEM_COUNT) {
continue;
}
- HgResolveStatusEnum status = HgResolveStatusEnum.valueOf(line.charAt(0));
+ final HgResolveStatusEnum status = HgResolveStatusEnum.valueOf(line.charAt(0));
if (status != null) {
File ioFile = new File(repo.getPath(), line.substring(2));
resolveStatus.put(new HgFile(repo, ioFile), status);
}
public void markResolved(VirtualFile repo, VirtualFile path) {
- HgCommandService.getInstance(project)
- .execute(repo, "resolve", Arrays.asList("--mark", path.getPath()));
+ HgCommandService.getInstance(myProject).execute(repo, "resolve", Arrays.asList("--mark", path.getPath()));
}
public void markResolved(VirtualFile repo, FilePath path) {
- HgCommandService.getInstance(project)
- .execute(repo, "resolve", Arrays.asList("--mark", path.getPath()));
- }
-
- public MergeData getResolveData(VirtualFile repo, VirtualFile path) throws VcsException {
- //start an hg resolve command, configured with a mercurial extension
- //which will transfer the contents of the base, local and other versions
- //to the socket server we set up on this end.
-
- if (FILEMERGE_PLUGIN == null) {
- throw new VcsException("Could not provide dynamic extension file");
- }
- Receiver receiver = new Receiver();
- SocketServer server = new SocketServer(receiver);
- try {
- int port = server.start();
-
- List<String> hgOptions = Arrays.asList(
- "--config", "extensions.hg4ideafilemerge=" + FILEMERGE_PLUGIN.getAbsolutePath(),
- "--config", "hg4ideafilemerge.port=" + port);
- ensureSuccess(HgCommandService.getInstance(project).
- execute(repo, hgOptions, "resolve", Arrays.asList(path.getPath())));
-
- } catch (IOException e) {
- throw new VcsException(e);
- }
- try {
- return receiver.getMergeParticipantsContents();
- } catch (InterruptedException e) {
- //operation was cancelled, never mind what the contents of all the participants is.
- return null;
- }
- }
-
- public static class Receiver extends SocketServer.Protocol{
-
- private MergeData data;
- private final CountDownLatch completed = new CountDownLatch(1);
-
- public boolean handleConnection(Socket socket) throws IOException {
- DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
- byte[] local = readDataBlock(dataInputStream);
- byte[] other = readDataBlock(dataInputStream);
- byte[] base = readDataBlock(dataInputStream);
-
- new PrintStream(socket.getOutputStream()).println("Done");
-
- data = new MergeData(local, other, base);
- completed.countDown();
- return false;
- }
-
- private MergeData getMergeParticipantsContents() throws InterruptedException, VcsException {
- //join defines a 'happens-before' relationship, so no need for extra synchronization
- completed.await(1, TimeUnit.SECONDS);
- if (data == null) {
- throw new VcsException("Did not receive data from Mercurial's resolve command");
- }
- return data;
- }
-
+ HgCommandService.getInstance(myProject).execute(repo, "resolve", Arrays.asList("--mark", path.getPath()));
}
- public final static class MergeData {
- private final byte[] local;
- private final byte[] other;
- private final byte[] base;
-
- private MergeData(byte[] local, byte[] other, byte[] base) {
- this.local = local;
- this.other = other;
- this.base = base;
- }
-
- public byte[] getLocal() {
- return local;
- }
-
- public byte[] getOther() {
- return other;
- }
-
- public byte[] getBase() {
- return base;
- }
- }
}
\ No newline at end of file
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.merge.MergeData;
import com.intellij.vcsUtil.VcsRunnable;
import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.NotNull;
+import org.zmlx.hg4idea.HgContentRevision;
+import org.zmlx.hg4idea.HgFile;
+import org.zmlx.hg4idea.HgRevisionNumber;
import org.zmlx.hg4idea.HgUtil;
import org.zmlx.hg4idea.command.HgResolveCommand;
+import org.zmlx.hg4idea.command.HgWorkingCopyRevisionsCommand;
/**
* @author Kirill Likhodedov
final MergeData mergeData = new MergeData();
final VcsRunnable runnable = new VcsRunnable() {
public void run() throws VcsException {
- final HgResolveCommand.MergeData resolveData = new HgResolveCommand(myProject).getResolveData(HgUtil.getHgRootOrThrow(myProject, file), file);
- mergeData.ORIGINAL = resolveData.getBase();
- mergeData.CURRENT = resolveData.getLocal();
- mergeData.LAST = resolveData.getOther();
+ // we have a merge in progress, which means we have 2 heads (parents).
+ // the latest one is "their" revision pulled from the parent repo,
+ // the earlier parent is the local change.
+ // to retrieve the base version we get the parent of the local change, i.e. the [only] parent of the second parent.
+ final HgWorkingCopyRevisionsCommand command = new HgWorkingCopyRevisionsCommand(myProject);
+ final Pair<HgRevisionNumber, HgRevisionNumber> parents = command.parents(HgUtil.getHgRootOrThrow(myProject, file), file);
+ // we are sure that we have a grandparent, because otherwise we'll get "repository is unrelated" error while pulling,
+ // due to different root changesets which is prohibited.
+ final HgRevisionNumber grandParent = command.parents(HgUtil.getHgRootOrThrow(myProject, file), file, parents.second).first;
+
+ final HgFile hgFile = new HgFile(myProject, file);
+ final HgContentRevision server = new HgContentRevision(myProject, hgFile, parents.first);
+ final HgContentRevision local = new HgContentRevision(myProject, hgFile, parents.second);
+ final HgContentRevision base = new HgContentRevision(myProject, hgFile, grandParent);
+
+ mergeData.ORIGINAL = base.getContentAsBytes();
+ mergeData.CURRENT = local.getContentAsBytes();
+ mergeData.LAST = server.getContentAsBytes();
}
};
VcsUtil.runVcsProcessWithProgress(runnable, VcsBundle.message("multiple.file.merge.loading.progress.title"), false, myProject);