changeset 703:3aff8caf7d60

add API to parse hg log --patch to recover .hgsub .hgsubstate for entire history
author eugene.petrenko@jetbrains.com
date Wed, 08 Jan 2014 16:37:57 +0100
parents dc12842c40e0
children 5ee94ee69b29
files mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LoadSubstatesCommand.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialMountPointsSupport.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgRepo.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialMountPointsSupportTest.java
diffstat 4 files changed, 219 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LoadSubstatesCommand.java	Wed Jan 08 16:37:57 2014 +0100
@@ -0,0 +1,83 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import com.intellij.openapi.diagnostic.Logger;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.diff.*;
+import jetbrains.buildServer.util.StringUtil;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * Created 03.01.14 14:53
+ *
+ * @author Eugene Petrenko (eugene.petrenko@jetbrains.com)
+ */
+public class LoadSubstatesCommand extends VcsRootCommand {
+  private static final Logger LOG = Logger.getInstance(LoadSubstatesCommand.class.getName());
+
+  public LoadSubstatesCommand(@NotNull final CommandSettings commandSettings,
+                              @NotNull final String hgPath,
+                              @NotNull final File workingDir,
+                              @NotNull final AuthSettings authSettings) {
+    super(commandSettings, hgPath, workingDir, authSettings);
+  }
+
+  @NotNull
+  private Set<String> cat(@NotNull final String commit,
+                          @NotNull final String file) throws VcsException {
+    final MercurialCommandLine cli = createCommandLine();
+    cli.addParameter("cat");
+    cli.addParameter("--rev");
+    cli.addParameter(commit);
+    cli.addParameter(file);
+
+    final CommandResult res = runCommand(cli);
+    return new LinkedHashSet<String>(Arrays.asList(StringUtil.splitByLines(res.getStdout())));
+  }
+
+  public void call(@NotNull final ContentProcessor consumer) throws VcsException {
+    final MercurialCommandLine cli = createCommandLine();
+    cli.addParameter("log");
+    cli.addParameter("--template=" + DiffParser.COMMITS_SEPARATOR + " {node|short} {p1node|short} {p2node|short}\\n");
+    cli.addParameter("--patch");
+    cli.addParameter(".hgsubstate");
+    cli.addParameter(".hgsub");
+
+    CommandResult res = runCommand(cli);
+    final String output = res.getStdout();
+
+    //TODO: reverse patches stream so to minimize used memory for changes graph
+
+    final DiffTree diff = new DiffTree() {
+      @NotNull
+      @Override
+      protected DiffFileTree createDiffFileTree(@NotNull final String file) {
+        return new DiffFileTree() {
+          @Override
+          protected void fetchContents(@NotNull List<String> commits, @NotNull Map<String, Set<String>> commitToLines) throws VcsException {
+            for (String commit : commits) {
+              LOG.debug("Fetching content for " + file + " @ " + commit + "...");
+              commitToLines.put(commit, cat(commit, file));
+            }
+          }
+        };
+      }
+    };
+    DiffParser.parse(lines(output), diff.processor());
+    diff.processDiffs(consumer);
+  }
+
+  private LinesIterator lines(@NotNull final String output) {
+    return new LinesIterator() {
+      final StringTokenizer st = new StringTokenizer(output, "\r\n");
+      @Nullable
+      public String nextLine() {
+        if (st.hasMoreTokens()) return st.nextToken();
+        return null;
+      }
+    };
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialMountPointsSupport.java	Wed Jan 08 16:37:57 2014 +0100
@@ -0,0 +1,50 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.diff.ContentProcessor;
+import jetbrains.buildServer.vcs.CheckoutRules;
+import jetbrains.buildServer.vcs.VcsException;
+import jetbrains.buildServer.vcs.VcsRoot;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+
+/**
+ * Created 03.01.14 13:57
+ *
+ * @author Eugene Petrenko (eugene.petrenko@jetbrains.com)
+ */
+public class MercurialMountPointsSupport implements MercurialServerExtension {
+  private final MercurialVcsSupport mySupport;
+  private final HgVcsRootFactory myHgVcsRootFactory;
+
+  public MercurialMountPointsSupport(@NotNull MercurialVcsSupport vcs,
+                                     @NotNull HgVcsRootFactory vcsRootFactory) {
+    vcs.addExtension(this);
+    mySupport = vcs;
+    myHgVcsRootFactory = vcsRootFactory;
+  }
+
+
+  public void collectMountPoints(@NotNull final VcsRoot root,
+                                 @NotNull final CheckoutRules rules,
+                                 @NotNull final Object consumer) throws VcsException {
+
+    final HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root);
+    final ServerHgRepo repo = mySupport.createRepo(hgRoot);
+
+    mySupport.syncRepository(hgRoot);
+
+    repo.loadSubstates(new ContentProcessor() {
+      public void processContent(@NotNull String commit, @NotNull String file, @NotNull Collection<String> lines) {
+        System.out.println(commit + "  " + file + "\n");
+        for (String line : lines) {
+          System.out.println("  " + line);
+        }
+        System.out.println();
+      }
+    });
+
+  }
+
+}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgRepo.java	Tue Jan 07 15:57:42 2014 +0100
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgRepo.java	Wed Jan 08 16:37:57 2014 +0100
@@ -2,6 +2,7 @@
 
 import com.intellij.openapi.util.Pair;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.diff.ContentProcessor;
 import jetbrains.buildServer.util.graph.DAG;
 import jetbrains.buildServer.util.graph.DAGs;
 import jetbrains.buildServer.vcs.ModificationData;
@@ -108,6 +109,10 @@
     return DAGs.createFromEdges(edges);
   }
 
+  public void loadSubstates(@NotNull final ContentProcessor processor) throws VcsException {
+    new LoadSubstatesCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings).call(processor);
+  }
+
   public Map<String, SubRepo> getSubrepositories(@NotNull ModificationData m) {
     if (hasSubrepoConfigChanges(m))
       return getSubrepositories(m.getVersion());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialMountPointsSupportTest.java	Wed Jan 08 16:37:57 2014 +0100
@@ -0,0 +1,81 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.vcs.CheckoutRules;
+import jetbrains.buildServer.vcs.VcsRoot;
+import jetbrains.buildServer.vcs.impl.VcsRootImpl;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.File;
+
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialSupportBuilder.mercurialSupport;
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerPluginConfigBuilder.serverPluginConfig;
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.copyRepository;
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder.vcsRoot;
+
+/**
+ * Created 03.01.14 14:10
+ *
+ * @author Eugene Petrenko (eugene.petrenko@jetbrains.com)
+ */
+@RequiredHgVersion(min = "1.7.0")
+@Test(dataProviderClass = HgVersionConstraint.class, dataProvider = "installedHgVersion")
+public class MercurialMountPointsSupportTest extends BaseMercurialTestCase {
+
+  private File myRemoteRepository;
+  private MercurialMountPointsSupport mySupport;
+  private MercurialVcsSupport myVcs;
+  private File myRemoteRepo1;
+  private File myRemoteRepo2;
+  private File myRemoteRepo3;
+  private VcsRootImpl myRoot;
+
+  @BeforeMethod
+  public void setUp() throws Exception {
+    super.setUp();
+    ServerPluginConfig config = serverPluginConfig()
+            .cachesDir(myTempFiles.createTempDir())
+            .hgPath(Util.getHgPath())
+            .build();
+
+    myRemoteRepository = myTempFiles.createTempDir();
+    Util.copyRepository(new File("mercurial-tests/testData/rep2"), myRemoteRepository);
+    MercurialSupportBuilder hgBuilder = mercurialSupport().withConfig(config);
+    MercurialVcsSupport vcs = hgBuilder.build();
+    myVcs = vcs;
+    mySupport = new MercurialMountPointsSupport(vcs, hgBuilder.getHgRootFactory());
+
+
+    File remoteRepoParentDir = myTempFiles.createTempDir();
+    myRemoteRepo1 = new File(remoteRepoParentDir, "r1");
+    myRemoteRepo2 = new File(remoteRepoParentDir, "r2");
+    myRemoteRepo3 = new File(remoteRepoParentDir, "r3");
+    copyRepository(new File("mercurial-tests/testData/subrepos/r1"), myRemoteRepo1);
+    copyRepository(new File("mercurial-tests/testData/subrepos/r2"), myRemoteRepo2);
+    copyRepository(new File("mercurial-tests/testData/subrepos/r3"), myRemoteRepo3);
+    ServerPluginConfig myPluginConfig = new ServerPluginConfigBuilder()
+            .cachesDir(myTempFiles.createTempDir())
+            .detectSubrepoChanges(true)
+            .build();
+    myVcs = mercurialSupport().withConfig(myPluginConfig).build();
+    myRoot = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
+  }
+
+  @Test(enabled = false)
+  public void should_return_commits_for_every_revision_in_state_local(HgVersion _) throws Exception {
+    VcsRoot root = vcsRoot().withUrl("F:\\Work\\ReSharper\\Psi.Features").build();
+    myVcs.syncRepository(root);
+
+    final long start = System.currentTimeMillis();
+    mySupport.collectMountPoints(root, CheckoutRules.DEFAULT, 42);
+
+    final long actual = System.currentTimeMillis() - start;
+    System.out.println("All mount points were computed in " + actual + " ms");
+  }
+
+  public void should_return_commits_for_every_revision_in_state(HgVersion _) throws Exception {
+    myVcs.syncRepository(myRoot);
+    mySupport.collectMountPoints(myRoot, CheckoutRules.DEFAULT, 42);
+  }
+
+}