[Mercurial] Rewrote the HgMergeProvider functionality: get revision contents directly...
authorKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Fri, 6 Aug 2010 11:31:37 +0000 (15:31 +0400)
committerKirill Likhodedov <kirill.likhodedov@jetbrains.com>
Fri, 6 Aug 2010 11:31:37 +0000 (15:31 +0400)
plugins/hg4idea/resources/python/filemerge.py [deleted file]
plugins/hg4idea/src/org/zmlx/hg4idea/command/HgResolveCommand.java
plugins/hg4idea/src/org/zmlx/hg4idea/provider/HgMergeProvider.java

diff --git a/plugins/hg4idea/resources/python/filemerge.py b/plugins/hg4idea/resources/python/filemerge.py
deleted file mode 100644 (file)
index 37994bb..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/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")
index 71f873f4a228e43434eb05266a8a465ca2061426..eb2e7e09e6b01a58a1463d520192b12a11cf9296 100644 (file)
@@ -14,49 +14,41 @@ package org.zmlx.hg4idea.command;
 
 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);
@@ -66,95 +58,11 @@ public class HgResolveCommand {
   }
 
   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
index 4074cf4518b971dbe9589e36ab9d096cffa6cfa6..12c37709171edd35ea28b5567a335797a5e8cc22 100644 (file)
@@ -17,6 +17,7 @@ package org.zmlx.hg4idea.provider;
 
 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;
@@ -25,8 +26,12 @@ import com.intellij.openapi.vfs.VirtualFile;
 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
@@ -45,10 +50,24 @@ public class HgMergeProvider implements MergeProvider {
     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);