changeset 631:d28d52336ce5

Implement MergeSupport
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Wed, 07 Aug 2013 13:08:31 +0400
parents 176552952302
children 83692f82fbb7
files mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommitCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ResolveCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/MergeConflictException.java mercurial-server-tc/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialExtensionRegistry.java mercurial-server/src/META-INF/build-server-plugin-mercurial.xml mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialMergeSupport.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialSupportBuilder.java
diffstat 11 files changed, 210 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java	Thu Aug 01 17:36:57 2013 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java	Wed Aug 07 13:08:31 2013 +0400
@@ -97,6 +97,18 @@
     return new ParentsCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir);
   }
 
+  public MergeCommand merge() {
+    return new MergeCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir);
+  }
+
+  public ResolveCommand resolve() {
+    return new ResolveCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir);
+  }
+
+  public CommitCommand commit() {
+    return new CommitCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir);
+  }
+
   public String path() {
     return myWorkingDir.getAbsolutePath();
   }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java	Thu Aug 01 17:36:57 2013 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java	Wed Aug 07 13:08:31 2013 +0400
@@ -54,6 +54,10 @@
     mySettings = settings;
   }
 
+  public int getExitCode() {
+    return myDelegate.getExitCode();
+  }
+
   @NotNull
   public String getStdout() {
     return removePrivateData(myDelegate.getStdout(), myPrivateData);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommitCommand.java	Wed Aug 07 13:08:31 2013 +0400
@@ -0,0 +1,30 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+public class CommitCommand extends BaseCommand {
+
+  private String myCommitMessage;
+
+  public CommitCommand(@NotNull CommandSettings commandSettings,
+                       @NotNull String hgPath,
+                       @NotNull File workingDir) {
+    super(commandSettings, hgPath, workingDir);
+  }
+
+  public CommitCommand message(@NotNull String commitMessage) {
+    myCommitMessage = commitMessage;
+    return this;
+  }
+
+  public void call() throws VcsException {
+    MercurialCommandLine cmd = createCommandLine();
+    cmd.addParameter("commit");
+    if (myCommitMessage != null)
+      cmd.addParameters("-m", myCommitMessage);
+    runCommand(cmd);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeCommand.java	Wed Aug 07 13:08:31 2013 +0400
@@ -0,0 +1,33 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.MergeConflictException;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+public class MergeCommand extends BaseCommand {
+
+  private String myRevision;
+
+  public MergeCommand(@NotNull CommandSettings commandSettings,
+                      @NotNull String hgPath,
+                      @NotNull File workingDir) {
+    super(commandSettings, hgPath, workingDir);
+  }
+
+  public MergeCommand revision(@NotNull String revision) {
+    myRevision = revision;
+    return this;
+  }
+
+  public void call() throws VcsException {
+    MercurialCommandLine cmd = createCommandLine();
+    cmd.addParameter("merge");
+    if (myRevision != null)
+      cmd.addParameters("-r", myRevision);
+    CommandResult result = runCommand(cmd);
+    if (result.getExitCode() == 1)
+      throw new MergeConflictException();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ResolveCommand.java	Wed Aug 07 13:08:31 2013 +0400
@@ -0,0 +1,29 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ResolveCommand extends BaseCommand {
+
+  public ResolveCommand(@NotNull CommandSettings commandSettings,
+                        @NotNull String hgPath,
+                        @NotNull File workingDir) {
+    super(commandSettings, hgPath, workingDir);
+  }
+
+  public List<String> call() throws VcsException {
+    MercurialCommandLine cmd = createCommandLine();
+    cmd.addParameters("resolve", "--no-status", "--list");
+    CommandResult result = runCommand(cmd);
+    String stdout = result.getStdout();
+    List<String> unresolvedFiles = new ArrayList<String>();
+    for (String line: stdout.split("[\r\n]+")) {
+      unresolvedFiles.add(line);
+    }
+    return unresolvedFiles;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/MergeConflictException.java	Wed Aug 07 13:08:31 2013 +0400
@@ -0,0 +1,9 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception;
+
+import jetbrains.buildServer.vcs.VcsException;
+
+public class MergeConflictException extends VcsException {
+  public MergeConflictException() {
+    super("Merge conflict");
+  }
+}
--- a/mercurial-server-tc/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialExtensionRegistry.java	Thu Aug 01 17:36:57 2013 +0400
+++ b/mercurial-server-tc/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialExtensionRegistry.java	Wed Aug 07 13:08:31 2013 +0400
@@ -8,6 +8,6 @@
 
   public MercurialExtensionRegistry(@NotNull MercurialVcsSupport vcs,
                                     @NotNull Collection<MercurialServerExtension> extensions) {
-    vcs.setExtensions(extensions);
+    vcs.addExtensions(extensions);
   }
 }
--- a/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml	Thu Aug 01 17:36:57 2013 +0400
+++ b/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml	Wed Aug 07 13:08:31 2013 +0400
@@ -11,4 +11,5 @@
   <bean id="testConnection" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.HgTestConnectionSupport" />
   <bean id="commandSettingsFactory" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerCommandSettingsFactory"/>
   <bean id="cleaner" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialCleaner"/>
+  <bean id="mergeSupport" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialMergeSupport"/>
 </beans>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialMergeSupport.java	Wed Aug 07 13:08:31 2013 +0400
@@ -0,0 +1,83 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.MergeConflictException;
+import jetbrains.buildServer.vcs.MergeOptions;
+import jetbrains.buildServer.vcs.MergeSupport;
+import jetbrains.buildServer.vcs.VcsException;
+import jetbrains.buildServer.vcs.VcsRoot;
+import org.jetbrains.annotations.NotNull;
+import com.intellij.openapi.diagnostic.Logger;
+
+import java.io.File;
+import java.util.List;
+
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.HgFileUtil.deleteDir;
+
+public class MercurialMergeSupport implements MergeSupport, MercurialServerExtension {
+
+  private final static Logger LOG = Logger.getInstance(MercurialMergeSupport.class.getName());
+
+  private final MercurialVcsSupport myVcs;
+  private final HgVcsRootFactory myHgVcsRootFactory;
+
+  public MercurialMergeSupport(@NotNull MercurialVcsSupport vcs,
+                               @NotNull HgVcsRootFactory vcsRootFactory) {
+    vcs.addExtension(this);
+    myVcs = vcs;
+    myHgVcsRootFactory = vcsRootFactory;
+  }
+
+  public void merge(@NotNull VcsRoot root,
+                    @NotNull String srcRevision,
+                    @NotNull String dstBranch,
+                    @NotNull String message,
+                    @NotNull MergeOptions options) throws VcsException {
+    File tmpDir = null;
+    try {
+      tmpDir = HgFileUtil.createTempDir();
+      HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root);
+      hgRoot.setCustomWorkingDir(tmpDir);
+      myVcs.syncRepository(hgRoot);
+      HgRepo repo = myVcs.createRepo(hgRoot);
+      repo.update().branch(dstBranch).call();
+
+      try {
+        repo.merge().revision(srcRevision).call();
+      } catch (MergeConflictException e) {
+        List<String> conflicts = repo.resolve().call();
+        throw new VcsException(getConflicsErrorMessage(conflicts));
+      }
+
+      repo.commit().message(message).call();
+
+      repo.push().toRepository(hgRoot.getRepository()).call();
+    } catch (Exception e) {
+      if (e instanceof VcsException)
+        throw (VcsException) e;
+      throw new VcsException(e);
+    } finally {
+      deleteDir(tmpDir, LOG);
+    }
+  }
+
+  @NotNull
+  private String getConflicsErrorMessage(@NotNull List<String> conflicts) {
+    if (conflicts.isEmpty())
+      return "Merge failed, but no conflicts found";
+
+    if (conflicts.size() == 1)
+      return "Failed to merge file " + conflicts.get(0);
+
+    StringBuilder message = new StringBuilder();
+    message.append("Failed to merge files: ");
+    int i = 0;
+    for (; i < conflicts.size() && i < 3; i++) {
+      message.append(conflicts.get(i)).append("\n");
+    }
+    if (i < conflicts.size())
+      message.append(" and ").append(conflicts.size() - i).append(" more");
+
+    return message.toString();
+  }
+}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Thu Aug 01 17:36:57 2013 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Wed Aug 07 13:08:31 2013 +0400
@@ -61,7 +61,7 @@
   private final FileFilter myAcceptAllFilter = new AcceptAllFilter();
   private final HgTestConnectionSupport myTestConnection;
   private final SubrepoCheckoutRulesProvider mySubrepoCheckoutRulesProvider;
-  private Collection<MercurialServerExtension> myExtensions;
+  private final Collection<MercurialServerExtension> myExtensions = new ArrayList<MercurialServerExtension>();
 
   public MercurialVcsSupport(@NotNull final EventDispatcher<ServerListener> dispatcher,
                              @NotNull final ResetCacheRegister resetCacheHandlerManager,
@@ -89,8 +89,12 @@
     logUsedHg();
   }
 
-  public void setExtensions(@NotNull Collection<MercurialServerExtension> extensions) {
-    myExtensions = extensions;
+  public void addExtensions(@NotNull Collection<MercurialServerExtension> extensions) {
+    myExtensions.addAll(extensions);
+  }
+
+  public void addExtension(@NotNull MercurialServerExtension extension) {
+    myExtensions.add(extension);
   }
 
   private void logUsedHg() {
@@ -648,8 +652,6 @@
   @Override
   @Nullable
   protected <T extends VcsExtension> T getVcsCustomExtension(@NotNull final Class<T> extensionClass) {
-    if (myExtensions == null)
-      return null;
     for (MercurialServerExtension e : myExtensions) {
       if (extensionClass.isInstance(e))
         return extensionClass.cast(e);
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialSupportBuilder.java	Thu Aug 01 17:36:57 2013 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialSupportBuilder.java	Wed Aug 07 13:08:31 2013 +0400
@@ -42,7 +42,7 @@
     }});
     MercurialVcsSupport vcs = new MercurialVcsSupport(dispatcher, resetCacheManager, myConfig, hgPathProvider,
             repoFactory, mirrorManager, myHgRootFactory, testConnection, new SubrepoCheckoutRulesProviderImpl());
-    vcs.setExtensions(myExtensions);
+    vcs.addExtensions(myExtensions);
     return vcs;
   }