changeset 511:f2666e817701

Detect changes in subrepos
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Thu, 15 Nov 2012 16:40:22 +0400
parents 35dabc364558
children 1533dd8058b8
files mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubRepoConfigChange.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/HgVcsRoot.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ParentsCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfig.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigImpl.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepoTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgPathProviderTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigBuilder.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubrepoChangesTest.java mercurial-tests/src/testng.xml mercurial-tests/testData/subrepos/README mercurial-tests/testData/subrepos/r1/hg/dirstate mercurial-tests/testData/subrepos/r1/hg/store/00changelog.i mercurial-tests/testData/subrepos/r1/hg/store/00manifest.i mercurial-tests/testData/subrepos/r1/hg/store/data/.hgsubstate.i mercurial-tests/testData/subrepos/r1/hg/store/undo mercurial-tests/testData/subrepos/r1/hg/undo.desc mercurial-tests/testData/subrepos/r1/hg/undo.dirstate mercurial-tests/testData/subrepos/r2/hg/dirstate mercurial-tests/testData/subrepos/r2/hg/store/00changelog.i mercurial-tests/testData/subrepos/r2/hg/store/00manifest.i mercurial-tests/testData/subrepos/r2/hg/store/data/b.i mercurial-tests/testData/subrepos/r2/hg/store/undo mercurial-tests/testData/subrepos/r3/hg/dirstate mercurial-tests/testData/subrepos/r3/hg/store/00changelog.i mercurial-tests/testData/subrepos/r3/hg/store/00manifest.i mercurial-tests/testData/subrepos/r3/hg/store/data/.hgsub.i mercurial-tests/testData/subrepos/r3/hg/store/data/.hgsubstate.i mercurial-tests/testData/subrepos/r3/hg/store/data/c.i mercurial-tests/testData/subrepos/r3/hg/store/fncache mercurial-tests/testData/subrepos/r3/hg/store/undo mercurial-tests/testData/subrepos/r3/hg/undo.dirstate
diffstat 36 files changed, 372 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java	Thu Nov 15 16:40:22 2012 +0400
@@ -1,5 +1,6 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial;
 
+import com.intellij.openapi.util.Trinity;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
 import jetbrains.buildServer.log.Loggers;
 import jetbrains.buildServer.util.FileUtil;
@@ -10,6 +11,7 @@
 import java.io.IOException;
 import java.util.*;
 
+import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyMap;
 import static jetbrains.buildServer.buildTriggers.vcs.mercurial.HgFileUtil.deleteDir;
 import static jetbrains.buildServer.util.FileUtil.isEmptyDir;
@@ -87,6 +89,10 @@
     return new VersionCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir);
   }
 
+  public ParentsCommand parents() {
+    return new ParentsCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir);
+  }
+
   public String path() {
     return myWorkingDir.getAbsolutePath();
   }
@@ -153,11 +159,79 @@
     return getSubrepositories(new ChangeSet(revision));
   }
 
+  public List<SubRepoConfigChange> getSubrepoConfigChanges(@NotNull String revision) throws VcsException {
+    if (containsSubrepoConfigChange(revision)) {
+      List<String> parents = parents().ofRevision(revision).call();
+      return getSubrepoConfigChanges(revision, parents.get(0));//what about merges?
+    }
+    return emptyList();
+  }
+
+  public List<SubRepoConfigChange> getSubrepoConfigChanges(@NotNull ChangeSet cset) {
+    if (containsSubrepoConfigChange(cset))
+      return getSubrepoConfigChanges(cset.getId(), cset.getParents().get(0).getId());
+    return emptyList();
+  }
+
+  private List<SubRepoConfigChange> getSubrepoConfigChanges(@NotNull String revision, @NotNull String parentRevision) {
+    Map<String, SubRepo> curSubrepos = getSubrepositories(revision);
+    Map<String, SubRepo> prevSubrepos = getSubrepositories(parentRevision);
+    return getSubrepoConfigChanges(prevSubrepos, curSubrepos);
+
+  }
+
+  private List<SubRepoConfigChange> getSubrepoConfigChanges(@NotNull Map<String, SubRepo> prevSubrepos,
+                                                            @NotNull Map<String, SubRepo> curSubrepos) {
+    List<SubRepoConfigChange> configChanges = new ArrayList<SubRepoConfigChange>();
+    for (Map.Entry<String, SubRepo> e : curSubrepos.entrySet()) {
+      SubRepo curSubrepo = e.getValue();
+      SubRepo prevSubrepo = prevSubrepos.remove(e.getKey());
+      if (prevSubrepo == null) {
+        configChanges.add(new SubRepoConfigChange(e.getKey(), null, curSubrepo));
+      } else {
+        String prevRevision = prevSubrepo.revision();
+        String currentRevision = curSubrepo.revision();
+        if (!currentRevision.equals(prevRevision))
+          configChanges.add(new SubRepoConfigChange(e.getKey(), prevSubrepo, curSubrepo));
+      }
+    }
+    for (Map.Entry<String, SubRepo> e : prevSubrepos.entrySet()) {
+      configChanges.add(new SubRepoConfigChange(e.getKey(), e.getValue(), null));
+    }
+    return configChanges;
+  }
+
+  private boolean containsSubrepoConfigChange(@NotNull ChangeSet cset) {
+    for (FileStatus f : cset.getModifiedFiles()) {
+      if (containsSubrepoConfigChange(f))
+        return true;
+    }
+    return false;
+  }
+
+  private boolean containsSubrepoConfigChange(@NotNull String revision) throws VcsException {
+    List<FileStatus> changedFiles = status()
+            .fromRevision(revision)
+            .toRevision(revision)
+            .showAllFiles()
+            .call();
+    for (FileStatus f : changedFiles) {
+      if (containsSubrepoConfigChange(f))
+        return true;
+    }
+    return false;
+  }
+
+  private boolean containsSubrepoConfigChange(@NotNull FileStatus f) {
+    return f.getPath().equals(".hgsubstate");
+  }
+
+  //path->subrepo
   public Map<String, SubRepo> getSubrepositories(@NotNull ChangeSet cset) {
     String revId = cset.getId();
     Map<String, SubRepo> subrepos = mySubreposCache.get(revId);
     if (subrepos != null)
-      return subrepos;
+      return new HashMap<String, SubRepo>(subrepos);
     CatCommand cc = cat();
     cc.setRevId(revId);
     File catDir = null;
@@ -167,7 +241,7 @@
       File hgsubstate = new File(catDir, ".hgsubstate");
       subrepos = readSubrepositories(hgsub, hgsubstate);
       mySubreposCache.put(revId, subrepos);
-      return subrepos;
+      return new HashMap<String, SubRepo>(subrepos);
     } catch (VcsException e) {
       return emptyMap();
     } finally {
@@ -190,15 +264,11 @@
           String path = entry.getKey();
           String url = entry.getValue();
           String revision = path2revision.get(path);
-          if (revision != null) {
+          if (revision != null)
             result.put(path, new SubRepo(this, path, url, revision));
-          } else {
-//              myLogger.warning("Cannot find revision for subrepository at path " + path + " skip it");
-          }
         }
         return result;
       } catch (IOException e) {
-//          myLogger.warning("Error while trying to read subrepositories " + e.getMessage());
         return emptyMap();
       }
     } else {
@@ -211,11 +281,8 @@
     Map<String, String> result = new HashMap<String, String>();
     for (String line : FileUtil.readFile(hgsub)) {
       String[] parts = line.split(" = ");
-      if (parts.length == 2) {
+      if (parts.length == 2)
         result.put(parts[0], parts[1]);
-      } else {
-//          myLogger.warning("Cannot parse the line '" + line + "' from .hgsub, skip it");
-      }
     }
     return result;
   }
@@ -226,11 +293,8 @@
     Map<String, String> result = new HashMap<String, String>();
     for (String line : FileUtil.readFile(hgsubstate)) {
       String[] parts = line.split(" ");
-      if (parts.length == 2) {
+      if (parts.length == 2)
         result.put(parts[1], parts[0]);
-      } else {
-//          myLogger.warning("Cannot parse the line '" + line + "' from .hgsubstate, skip it");
-      }
     }
     return result;
   }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubRepoConfigChange.java	Thu Nov 15 16:40:22 2012 +0400
@@ -0,0 +1,37 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * A change in subrepo configuration
+ */
+public class SubRepoConfigChange {
+
+  private final String myPath;
+  private final SubRepo myPrevious;
+  private final SubRepo myCurrent;
+
+  public SubRepoConfigChange(@NotNull String path,
+                             @Nullable SubRepo previous,
+                             @Nullable SubRepo current) {
+    myPath = path;
+    myPrevious = previous;
+    myCurrent = current;
+  }
+
+  @NotNull
+  public String getPath() {
+    return myPath;
+  }
+
+  @Nullable
+  public SubRepo getPrevious() {
+    return myPrevious;
+  }
+
+  @Nullable
+  public SubRepo getCurrent() {
+    return myCurrent;
+  }
+}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java	Thu Nov 15 16:40:22 2012 +0400
@@ -15,6 +15,7 @@
  */
 package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
 
+import jetbrains.buildServer.vcs.VcsException;
 import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
@@ -58,6 +59,10 @@
     return cl;
   }
 
+  protected CommandResult runCommand(@NotNull MercurialCommandLine cli) throws VcsException {
+    return CommandUtil.runCommand(cli, myCommandSettings);
+  }
+
   protected Set<String> getPrivateData() {
     return emptySet();
   }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/HgVcsRoot.java	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/HgVcsRoot.java	Thu Nov 15 16:40:22 2012 +0400
@@ -43,8 +43,12 @@
   private File myCustomWorkingDir;
 
   public HgVcsRoot(@NotNull final VcsRoot vcsRoot) {
+    this(vcsRoot, vcsRoot.getProperty(Constants.REPOSITORY_PROP));
+  }
+
+  public HgVcsRoot(@NotNull VcsRoot vcsRoot, @NotNull String repository) {
     myRoot = vcsRoot;
-    myRepository = getProperty(Constants.REPOSITORY_PROP);
+    myRepository = repository;
     myHgCommandPath = getProperty(Constants.HG_COMMAND_PATH_PROP);
     myBranchName = getProperty(Constants.BRANCH_NAME_PROP);
     myCustomClonePath = getProperty(Constants.SERVER_CLONE_PATH_PROP);
@@ -61,6 +65,10 @@
     return myRepository;
   }
 
+  public HgVcsRoot withUrl(@NotNull String repositoryUrl) {
+    return new HgVcsRoot(this, repositoryUrl);
+  }
+
   /**
    * Returns name of the branch to use (returns 'default' if no branch specified)
    * @return see above
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ParentsCommand.java	Thu Nov 15 16:40:22 2012 +0400
@@ -0,0 +1,38 @@
+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 ParentsCommand extends BaseCommand {
+
+  private String myRevision;
+
+  public ParentsCommand(@NotNull CommandSettings commandSettings,
+                        @NotNull String hgPath,
+                        @NotNull File workingDir) {
+    super(commandSettings, hgPath, workingDir);
+  }
+
+  public ParentsCommand ofRevision(@NotNull String revision) {
+    myRevision = revision;
+    return this;
+  }
+
+  public List<String> call() throws VcsException {
+    MercurialCommandLine cli = createCommandLine();
+    cli.addParameter("parents");
+    cli.addParameter("-q");
+    if (myRevision != null)
+      cli.addParameters("-r", myRevision);
+    CommandResult res = runCommand(cli);
+    List<String> parentRevisions = new ArrayList<String>();
+    for (String line : res.getStdout().split("\n")) {
+      parentRevisions.add(new ChangeSet(line).getId());
+    }
+    return parentRevisions;
+  }
+}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java	Thu Nov 15 16:40:22 2012 +0400
@@ -23,10 +23,6 @@
   }
 
 
-  protected CommandResult runCommand(@NotNull MercurialCommandLine cli) throws VcsException {
-    return CommandUtil.runCommand(cli, myCommandSettings);
-  }
-
   protected CommandResult runCommand(@NotNull MercurialCommandLine cli, @NotNull CommandSettings s) throws VcsException {
     s.setPrivateData(getPrivateData());
     return CommandUtil.runCommand(cli, s);
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Thu Nov 15 16:40:22 2012 +0400
@@ -15,6 +15,7 @@
  */
 package jetbrains.buildServer.buildTriggers.vcs.mercurial;
 
+import com.intellij.openapi.util.Trinity;
 import jetbrains.buildServer.BuildAgent;
 import jetbrains.buildServer.Used;
 import jetbrains.buildServer.buildTriggers.vcs.AbstractVcsPropertiesProcessor;
@@ -590,7 +591,8 @@
     String fromCommit = new ChangeSetRevision(fromVersion).getId();
     String toCommit = new ChangeSetRevision(toVersion).getId();
     try {
-      List<ChangeSet> changesets = createRepo(root).collectChanges(root)
+      ServerHgRepo repo = createRepo(root);
+      List<ChangeSet> changesets = repo.collectChanges(root)
               .fromRevision(fromCommit)
               .toRevision(toCommit)
               .call();
@@ -600,6 +602,8 @@
         if (cset.getId().equals(fromCommit))
           iter.remove();//skip already reported changes
       }
+      if (myConfig.detectSubrepoChanges())
+        changesets.addAll(getSubrepoChangesets(root, repo, changesets));
       return changesets;
     } catch (UnknownRevisionException e) {
       Loggers.VCS.warn("Revision '" + e.getRevision() + "' is unknown, will return no changes");
@@ -607,6 +611,26 @@
     }
   }
 
+  @NotNull
+  private List<ChangeSet> getSubrepoChangesets(@NotNull HgVcsRoot root,
+                                               @NotNull HgRepo repo,
+                                               @NotNull List<ChangeSet> csets) throws VcsException {
+    List<ChangeSet> subrepoCsets = new ArrayList<ChangeSet>();
+    for (ChangeSet cset : csets) {
+      List<SubRepoConfigChange> subrepoConfigChanges = repo.getSubrepoConfigChanges(cset);
+      for (SubRepoConfigChange c : subrepoConfigChanges) {
+        if (c.getCurrent() != null && c.getPrevious() != null && c.getCurrent().url().equals(c.getPrevious().url())) {
+          String subrepoUrl = c.getCurrent().isRelative() ? root.getRepository() + "/" + c.getCurrent().url() : c.getCurrent().url();
+          String fromRevision = c.getPrevious().revision();
+          String toRevision = c.getCurrent().revision();
+          HgVcsRoot subrepoRoot = root.withUrl(subrepoUrl);
+          syncRepository(subrepoRoot);
+          subrepoCsets.addAll(getChangesets(subrepoRoot, fromRevision, toRevision));
+        }
+      }
+    }
+    return subrepoCsets;
+  }
 
   @NotNull
   public BuildPatchPolicy getBuildPatchPolicy() {
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfig.java	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfig.java	Thu Nov 15 16:40:22 2012 +0400
@@ -21,4 +21,6 @@
 
   @NotNull
   Set<Long> getRevsetParentRootIds();
+
+  public boolean detectSubrepoChanges();
 }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigImpl.java	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigImpl.java	Thu Nov 15 16:40:22 2012 +0400
@@ -67,4 +67,8 @@
     }
     return ids;
   }
+
+  public boolean detectSubrepoChanges() {
+    return false;
+  }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepoTest.java	Thu Nov 15 16:40:22 2012 +0400
@@ -0,0 +1,65 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import com.intellij.openapi.diagnostic.Logger;
+import jetbrains.buildServer.TempFiles;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.AuthSettings;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.TestCommandSettingsFactory;
+import jetbrains.buildServer.log.Log4jFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.copyRepository;
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertNull;
+import static org.testng.AssertJUnit.assertTrue;
+
+@Test
+public class HgRepoTest {
+
+  static {
+    Logger.setFactory(new Log4jFactory());
+  }
+
+  private TempFiles myTempFiles;
+
+  @BeforeMethod
+  public void setUp() throws IOException {
+    myTempFiles = new TempFiles();
+  }
+
+  @AfterMethod
+  public void tearDown() {
+    myTempFiles.cleanup();
+  }
+
+  public void subrepos() throws Exception {
+    File repository = myTempFiles.createTempDir();
+    copyRepository(new File("mercurial-tests/testData/subrepos/r1"), repository);
+    HgRepo repo = new HgRepo(new TestCommandSettingsFactory(), repository, Util.getHgPath(), new AuthSettings());
+    List<SubRepoConfigChange> changes = repo.getSubrepoConfigChanges("09c256b6163e");
+    assertEquals(1, changes.size());
+    SubRepoConfigChange c = changes.get(0);
+    assertEquals("r2", c.getPath());
+    //noinspection ConstantConditions
+    assertTrue(c.getPrevious().revision().startsWith("9e4a2fef1a1c"));
+    //noinspection ConstantConditions
+    assertTrue(c.getCurrent().revision().startsWith("ebb884b1b691"));
+
+    changes = repo.getSubrepoConfigChanges("4d7b3db8779f");
+    assertEquals(1, changes.size());
+    c = changes.get(0);
+    assertEquals("r2", c.getPath());
+    //noinspection ConstantConditions
+    assertTrue(c.getPrevious().revision().startsWith("916933c1dd8e"));
+    assertNull(c.getCurrent());
+
+    changes = repo.getSubrepoConfigChanges("d350e7209906");
+    assertEquals(1, changes.size());
+  }
+
+}
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgPathProviderTest.java	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgPathProviderTest.java	Thu Nov 15 16:40:22 2012 +0400
@@ -46,7 +46,7 @@
 
 
   private HgVcsRoot createHgRoot() throws Exception {
-    return new HgVcsRoot(new VcsRootBuilder().withHgPath(myVcsRootHgPath).build());
+    return new HgVcsRoot(new VcsRootBuilder().withUrl("some url").withHgPath(myVcsRootHgPath).build());
   }
 
 }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigBuilder.java	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigBuilder.java	Thu Nov 15 16:40:22 2012 +0400
@@ -15,6 +15,7 @@
   private String myHgPath;
   private File myCachesDir;
   private boolean myDontUseRevsets = false;
+  private boolean myDetectSubrepoChanges = false;
 
   @NotNull
   public ServerPluginConfig build() {
@@ -47,6 +48,10 @@
       public Set<Long> getRevsetParentRootIds() {
         return new HashSet<Long>();
       }
+
+      public boolean detectSubrepoChanges() {
+        return myDetectSubrepoChanges;
+      }
     };
   }
 
@@ -70,4 +75,9 @@
     myDontUseRevsets = true;
     return this;
   }
+
+  public ServerPluginConfigBuilder detectSubrepoChanges(boolean doDetect) {
+    myDetectSubrepoChanges = doDetect;
+    return this;
+  }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubrepoChangesTest.java	Thu Nov 15 16:40:22 2012 +0400
@@ -0,0 +1,84 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import com.intellij.openapi.diagnostic.Logger;
+import jetbrains.buildServer.TempFiles;
+import jetbrains.buildServer.log.Log4jFactory;
+import jetbrains.buildServer.vcs.CheckoutRules;
+import jetbrains.buildServer.vcs.ModificationData;
+import jetbrains.buildServer.vcs.VcsRoot;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialSupportBuilder.mercurialSupport;
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.copyRepository;
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder.vcsRoot;
+import static org.testng.AssertJUnit.assertEquals;
+
+@Test
+public class SubrepoChangesTest {
+
+  static {
+    Logger.setFactory(new Log4jFactory());
+  }
+
+  private TempFiles myTempFiles;
+  private MercurialVcsSupport myVcs;
+  private File myRemoteRepo1;
+  private File myRemoteRepo2;
+  private File myRemoteRepo3;
+
+  @BeforeMethod
+  public void setUp() throws IOException {
+    myTempFiles = new TempFiles();
+    ServerPluginConfig pluginConfig = new ServerPluginConfigBuilder()
+            .cachesDir(myTempFiles.createTempDir())
+            .detectSubrepoChanges(true)
+            .build();
+    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);
+    myVcs = mercurialSupport().withConfig(pluginConfig).build();
+  }
+
+  @AfterMethod
+  public void tearDown() {
+    myTempFiles.cleanup();
+  }
+
+
+  public void should_report_changes_from_subrepos() throws Exception {
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).build();
+    List<ModificationData> changes = myVcs.collectChanges(root, "d350e7209906", "09c256b6163e", CheckoutRules.DEFAULT);
+    assertEquals(3, changes.size());
+  }
+
+
+  public void should_not_report_any_changes_when_subrepo_removed() throws Exception {
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).build();
+    List<ModificationData> changes = myVcs.collectChanges(root, "34017377d9c3", "4d7b3db8779f", CheckoutRules.DEFAULT);
+    assertEquals(1, changes.size());
+  }
+
+
+  public void should_not_report_any_changes_when_subrepo_added() throws Exception {
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).build();
+    List<ModificationData> changes = myVcs.collectChanges(root, "4d7b3db8779f", "d350e7209906", CheckoutRules.DEFAULT);
+    assertEquals(1, changes.size());
+  }
+
+
+  public void should_report_subrepo_changes_recursevly() throws  Exception {
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).build();
+    List<ModificationData> changes = myVcs.collectChanges(root, "09c256b6163e", "d64d9799c143", CheckoutRules.DEFAULT);
+    assertEquals(5, changes.size());
+  }
+}
--- a/mercurial-tests/src/testng.xml	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-tests/src/testng.xml	Thu Nov 15 16:40:22 2012 +0400
@@ -26,6 +26,8 @@
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.ListFilesSupportTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.RevisionFormatTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.HgFileUtilTest"/>
+      <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.SubrepoChangesTest"/>
+      <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.HgRepoTest"/>
     </classes>
   </test>
 </suite>
--- a/mercurial-tests/testData/subrepos/README	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-tests/testData/subrepos/README	Thu Nov 15 16:40:22 2012 +0400
@@ -1,13 +1,21 @@
 r1 history:
+5:d64d9799c143 Recursive subrepos                           <- subrepository r2 = ../r3 (514c3e09cddf)
+4:09c256b6163e Subrepo update                               <- subrepository r2 = ../r3 (ebb884b1b691)
 3:d350e7209906 Add different subrepository in the same path <- subrepository r2 = ../r3 (9e4a2fef1a1c)
 2:4d7b3db8779f Remove subrepository                         <- subrepository removed
 1:34017377d9c3 Add subrepository                            <- subrepository r2 = ../r2 (916933c1dd8e)
 0:e4eced2b7381 Initial commit
 
 r2 history:
+2:ac0003deae69 three
+1:29053b4b29ce two
 0:916933c1dd8e Initial commit
 
 r3 history:
+4:514c3e09cddf Upgrade subrepo                              <- subrepository r2 = ../r2 (ac0003deae69)
+3:1f9eb39a3921 Add subrepo                                  <- subrepository r2 = ../r2 (916933c1dd8e)
+2:ebb884b1b691 two
+1:f41168ce47b7 one
 0:9e4a2fef1a1c Initial commit
 
 
Binary file mercurial-tests/testData/subrepos/r1/hg/dirstate has changed
Binary file mercurial-tests/testData/subrepos/r1/hg/store/00changelog.i has changed
Binary file mercurial-tests/testData/subrepos/r1/hg/store/00manifest.i has changed
Binary file mercurial-tests/testData/subrepos/r1/hg/store/data/.hgsubstate.i has changed
Binary file mercurial-tests/testData/subrepos/r1/hg/store/undo has changed
--- a/mercurial-tests/testData/subrepos/r1/hg/undo.desc	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-tests/testData/subrepos/r1/hg/undo.desc	Thu Nov 15 16:40:22 2012 +0400
@@ -1,2 +1,2 @@
-3
+4
 commit
Binary file mercurial-tests/testData/subrepos/r1/hg/undo.dirstate has changed
Binary file mercurial-tests/testData/subrepos/r2/hg/dirstate has changed
Binary file mercurial-tests/testData/subrepos/r2/hg/store/00changelog.i has changed
Binary file mercurial-tests/testData/subrepos/r2/hg/store/00manifest.i has changed
Binary file mercurial-tests/testData/subrepos/r2/hg/store/data/b.i has changed
Binary file mercurial-tests/testData/subrepos/r2/hg/store/undo has changed
Binary file mercurial-tests/testData/subrepos/r3/hg/dirstate has changed
Binary file mercurial-tests/testData/subrepos/r3/hg/store/00changelog.i has changed
Binary file mercurial-tests/testData/subrepos/r3/hg/store/00manifest.i has changed
Binary file mercurial-tests/testData/subrepos/r3/hg/store/data/.hgsub.i has changed
Binary file mercurial-tests/testData/subrepos/r3/hg/store/data/.hgsubstate.i has changed
Binary file mercurial-tests/testData/subrepos/r3/hg/store/data/c.i has changed
--- a/mercurial-tests/testData/subrepos/r3/hg/store/fncache	Mon Nov 12 21:20:00 2012 +0400
+++ b/mercurial-tests/testData/subrepos/r3/hg/store/fncache	Thu Nov 15 16:40:22 2012 +0400
@@ -1,1 +1,3 @@
+data/.hgsubstate.i
+data/.hgsub.i
 data/c.i
Binary file mercurial-tests/testData/subrepos/r3/hg/store/undo has changed
Binary file mercurial-tests/testData/subrepos/r3/hg/undo.dirstate has changed