changeset 548:6c8911ed66f2

Report subrepo changes
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Thu, 21 Feb 2013 18:46:43 +0400
parents 42aeecf691f9
children 80a45e45918c
files mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgSubrepoConfigChange.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubRepoConfigChange.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesCommand.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesNoRevsets.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesWithRevsets.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepoTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubrepoChangesTest.java
diffstat 9 files changed, 258 insertions(+), 197 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java	Thu Feb 21 17:40:32 2013 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java	Thu Feb 21 18:46:43 2013 +0400
@@ -1,6 +1,5 @@
 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;
@@ -115,6 +114,10 @@
     FileUtil.delete(new File(dotHg, "bookmarks.current"));
   }
 
+  public String getHgPath() {
+    return myHgPath;
+  }
+
   @NotNull
   public List<String> listFiles(@NotNull String revision) throws VcsException {
     List<FileStatus> fileStatuses = status()
@@ -173,44 +176,52 @@
     return getSubrepositories(new ChangeSet(revision));
   }
 
-  public List<SubRepoConfigChange> getSubrepoConfigChanges(@NotNull String revision) throws VcsException {
+  public List<HgSubrepoConfigChange> 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 getSubrepoConfigChanges(revision, parents);
     }
     return emptyList();
   }
 
-  public List<SubRepoConfigChange> getSubrepoConfigChanges(@NotNull ChangeSet cset) {
-    if (containsSubrepoConfigChange(cset))
-      return getSubrepoConfigChanges(cset.getId(), cset.getParents().get(0).getId());
+  public List<HgSubrepoConfigChange> getSubrepoConfigChanges(@NotNull ChangeSet cset) {
+    if (containsSubrepoConfigChange(cset)) {
+      List<String> parents = new ArrayList<String>();
+      for (ChangeSetRevision p : cset.getParents()) {
+        parents.add(p.getId());
+      }
+      return getSubrepoConfigChanges(cset.getId(), parents);
+    }
     return emptyList();
   }
 
-  private List<SubRepoConfigChange> getSubrepoConfigChanges(@NotNull String revision, @NotNull String parentRevision) {
+  private List<HgSubrepoConfigChange> getSubrepoConfigChanges(@NotNull String revision, @NotNull List<String> parentRevisions) {
     Map<String, SubRepo> curSubrepos = getSubrepositories(revision);
-    Map<String, SubRepo> prevSubrepos = getSubrepositories(parentRevision);
+    List<Map<String, SubRepo>> prevSubrepos = new ArrayList<Map<String, SubRepo>>();
+    for (String parentRevision : parentRevisions) {
+      prevSubrepos.add(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>();
+  private List<HgSubrepoConfigChange> getSubrepoConfigChanges(@NotNull List<Map<String, SubRepo>> prevSubrepos,
+                                                              @NotNull Map<String, SubRepo> curSubrepos) {
+    List<HgSubrepoConfigChange> configChanges = new ArrayList<HgSubrepoConfigChange>();
     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));
+      List<SubRepo> prevs = new ArrayList<SubRepo>();
+      for (Map<String, SubRepo> prev : prevSubrepos) {
+        SubRepo prevSubrepo = prev.remove(e.getKey());
+        if (prevSubrepo != null)
+          prevs.add(prevSubrepo);
       }
+      configChanges.add(new HgSubrepoConfigChange(e.getKey(), prevs, curSubrepo));
     }
-    for (Map.Entry<String, SubRepo> e : prevSubrepos.entrySet()) {
-      configChanges.add(new SubRepoConfigChange(e.getKey(), e.getValue(), null));
+    for (Map<String, SubRepo> prev : prevSubrepos) {
+      for (Map.Entry<String, SubRepo> e : prev.entrySet()) {
+        configChanges.add(new HgSubrepoConfigChange(e.getKey(), e.getValue(), null));
+      }
     }
     return configChanges;
   }
@@ -279,7 +290,7 @@
           String url = entry.getValue();
           String revision = path2revision.get(path);
           if (revision != null)
-            result.put(path, new SubRepo(this, path, url, revision));
+            result.put(path, new SubRepo(path, url, revision));
         }
         return result;
       } catch (IOException e) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgSubrepoConfigChange.java	Thu Feb 21 18:46:43 2013 +0400
@@ -0,0 +1,67 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+
+/**
+ * A change in subrepo configuration
+ */
+public class HgSubrepoConfigChange {
+
+  private final String myPath;
+  private final SubRepo myCurrent;
+  private final List<SubRepo> myPrevious;
+
+  public HgSubrepoConfigChange(@NotNull String path,
+                               @Nullable SubRepo previous,
+                               @Nullable SubRepo current) {
+    this(path, previous != null ? asList(previous) : new ArrayList<SubRepo>(), current);
+  }
+
+  public HgSubrepoConfigChange(@NotNull String path,
+                               @NotNull List<SubRepo> previous,
+                               @Nullable SubRepo current) {
+    myPath = path;
+    myPrevious = previous;
+    myCurrent = current;
+  }
+
+  @NotNull
+  public String getPath() {
+    return myPath;
+  }
+
+  @NotNull
+  public List<SubRepo> getPrevious() {
+    return myPrevious;
+  }
+
+  public SubRepo getCurrent() {
+    return myCurrent;
+  }
+
+  public boolean subrepoRemoved() {
+    return myCurrent == null && !myPrevious.isEmpty();
+  }
+
+  public boolean subrepoAdded() {
+    return myCurrent != null && myPrevious.isEmpty();
+  }
+
+  public boolean subrepoUrlChanged() {
+    if (myCurrent == null)
+      return false;
+    if (myPrevious.isEmpty())
+      return false;
+    for (SubRepo sr : myPrevious) {
+      if (!myCurrent.url().equals(sr.url()))
+        return true;
+    }
+    return false;
+  }
+}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubRepoConfigChange.java	Thu Feb 21 17:40:32 2013 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +0,0 @@
-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-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Thu Feb 21 17:40:32 2013 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Thu Feb 21 18:46:43 2013 +0400
@@ -27,6 +27,7 @@
 import jetbrains.buildServer.util.FileUtil;
 import jetbrains.buildServer.util.cache.ResetCacheRegister;
 import jetbrains.buildServer.vcs.*;
+import jetbrains.buildServer.vcs.impl.VcsRootImpl;
 import jetbrains.buildServer.vcs.patches.PatchBuilder;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -35,8 +36,10 @@
 import java.io.FileFilter;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.net.URISyntaxException;
 import java.util.*;
 
+import static java.util.Arrays.asList;
 import static java.util.Collections.emptyMap;
 import static jetbrains.buildServer.buildTriggers.vcs.mercurial.HgFileUtil.deleteDir;
 
@@ -537,19 +540,34 @@
   }
 
   public List<ModificationData> collectChanges(@NotNull VcsRoot root, @NotNull String fromVersion, @Nullable String currentVersion, @NotNull CheckoutRules checkoutRules) throws VcsException {
+    OperationContext ctx = new OperationContext(this);
+    return collectChanges(ctx, root, asList(fromVersion), currentVersion, checkoutRules);
+  }
+
+  private List<ModificationData> collectChanges(@NotNull OperationContext ctx,
+                                                @NotNull VcsRoot root,
+                                                @NotNull List<String> fromVersion,
+                                                @Nullable String currentVersion,
+                                                @NotNull CheckoutRules checkoutRules) throws VcsException {
     HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root);
-    syncRepository(hgRoot);
+    ctx.syncRepository(hgRoot);
     List<ModificationData> result = new ArrayList<ModificationData>();
-    for (ChangeSet cset : getChangesets(hgRoot, fromVersion, currentVersion)) {
-      result.add(createModificationData(cset, root, checkoutRules));
+    for (ChangeSet cset : getChangesets(ctx, hgRoot, fromVersion, currentVersion)) {
+      result.add(createModificationData(ctx, cset, root, checkoutRules));
     }
-    if (myConfig.reportSubrepoConfigUpdates())
-      addSubrepoConfigChanges(hgRoot, result);
+
+    if (myConfig.detectSubrepoChanges() && hgRoot.detectSubrepoChanges()) {
+      List<ModificationData> subrepoChanges = new ArrayList<ModificationData>();
+      for (ModificationData m : result) {
+        subrepoChanges.addAll(getSubrepoChanges(ctx, m));
+      }
+      result.addAll(subrepoChanges);
+    }
     return result;
   }
 
 
-  private ModificationData createModificationData(@NotNull final ChangeSet cset, @NotNull final VcsRoot root, @NotNull final CheckoutRules checkoutRules) {
+  private ModificationData createModificationData(@NotNull OperationContext ctx, @NotNull final ChangeSet cset, @NotNull final VcsRoot root, @NotNull final CheckoutRules checkoutRules) throws VcsException {
     List<ChangeSetRevision> parents = cset.getParents();
     if (parents.isEmpty())
       throw new IllegalStateException("Commit " + cset.getId() + " has no parents");
@@ -559,6 +577,7 @@
       result.addParentRevision(parent.getId());
     }
     setCanBeIgnored(result, cset);
+    result.setAttributes(getAttributes(root, cset));
     return result;
   }
 
@@ -575,96 +594,78 @@
 
 
   @NotNull
-  private List<ChangeSet> getChangesets(@NotNull final HgVcsRoot root, @NotNull final String fromVersion, @Nullable final String toVersion) throws VcsException {
+  private List<ChangeSet> getChangesets(@NotNull OperationContext ctx, @NotNull final HgVcsRoot root, @NotNull final List<String> fromVersions, @Nullable final String toVersion) throws VcsException {
     if (toVersion == null)
       return Collections.emptyList();
-    String fromCommit = new ChangeSetRevision(fromVersion).getId();
+    List<String> fromCommits = new ArrayList<String>();
+    for (String fromVersion : fromVersions) {
+      fromCommits.add(new ChangeSetRevision(fromVersion).getId());
+    }
     String toCommit = new ChangeSetRevision(toVersion).getId();
     try {
-      ServerHgRepo repo = createRepo(root);
-      List<ChangeSet> changesets = repo.collectChanges(root)
-              .fromRevision(fromCommit)
+      ServerHgRepo repo = createRepo(ctx, root);
+      return repo.collectChanges(root)
+              .fromRevision(fromCommits)
               .toRevision(toCommit)
               .call();
-      Iterator<ChangeSet> iter = changesets.iterator();
-      while (iter.hasNext()) {
-        ChangeSet cset = iter.next();
-        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");
       return Collections.emptyList();
     }
   }
 
-  private void addSubrepoConfigChanges(@NotNull HgVcsRoot root, @NotNull List<ModificationData> changes) {
-    try {
-      ServerHgRepo repo = createRepo(root);
-      for (ModificationData change : changes) {
-        List<SubRepoConfigChange> subrepoConfigChanges = repo.getSubrepoConfigChanges(change.getVersion());
-        int i = 1;
-        for (SubRepoConfigChange c : subrepoConfigChanges) {
-          String subrepoUrl = null;
-          String curRevision = null;
-          String prevRevision = null;
-          if (c.getCurrent() == null && c.getPrevious() != null) {
-            subrepoUrl = c.getPrevious().resolveUrl(root.getRepository());
-            HgVcsRoot subrepoRoot = root.withUrl(subrepoUrl);
-            syncRepository(subrepoRoot);
-            ServerHgRepo subrepo = createRepo(subrepoRoot);
-            prevRevision = subrepo.id().revision(c.getPrevious().revision()).inLocalRepository().call();
-          } else if (c.getPrevious() == null) {
-            subrepoUrl = c.getCurrent().resolveUrl(root.getRepository());
-            HgVcsRoot subrepoRoot = root.withUrl(subrepoUrl);
-            syncRepository(subrepoRoot);
-            ServerHgRepo subrepo = createRepo(subrepoRoot);
-            curRevision = subrepo.id().revision(c.getCurrent().revision()).inLocalRepository().call();
-          } else if (c.getCurrent().url().equals(c.getPrevious().url())) {
-            subrepoUrl = c.getCurrent().resolveUrl(root.getRepository());
-            HgVcsRoot subrepoRoot = root.withUrl(subrepoUrl);
-            syncRepository(subrepoRoot);
-            ServerHgRepo subrepo = createRepo(subrepoRoot);
-            prevRevision = subrepo.id().revision(c.getPrevious().revision()).inLocalRepository().call();
-            curRevision = subrepo.id().revision(c.getCurrent().revision()).inLocalRepository().call();
-          }
-          change.setAttribute("subrepo." + i + ".path", c.getPath());
-          if (subrepoUrl != null)
-            change.setAttribute("subrepo." + i + ".rootDiff." + Constants.REPOSITORY_PROP, subrepoUrl);
-          if (prevRevision != null)
-            change.setAttribute("subrepo." + i + ".oldRevision", prevRevision);
-          if (curRevision != null)
-            change.setAttribute("subrepo." + i + ".newRevision", curRevision);
-          i++;
+  @NotNull
+  private Map<String, String> getAttributes(@NotNull VcsRoot mainRoot, @NotNull ChangeSet cset) throws VcsException {
+    Map<String, String> attributes = new HashMap<String, String>();
+    HgVcsRoot root = myHgVcsRootFactory.createHgRoot(mainRoot);
+    if (myConfig.detectSubrepoChanges() && root.detectSubrepoChanges()) {
+      try {
+        ServerHgRepo repo = createRepo(root);
+        SubrepoConfigChangesAttributes builder = new SubrepoConfigChangesAttributes();
+        for (HgSubrepoConfigChange c : repo.getSubrepoConfigChanges(cset)) {
+          fillSubrepoConfigChanges(builder, root, c);
         }
+        attributes.putAll(builder.buildAttributes());
+      } catch (Exception e) {
+        Loggers.VCS.warn("Error while reporting subrepo config changes", e);
+        if (e instanceof VcsExtension)
+          throw (VcsException) e;
+        throw new VcsException(e);
       }
-    } catch (Exception e) {
-      Loggers.VCS.warn("Error while reporting subrepo config changes", e);
+    }
+    return attributes;
+  }
+
+  private void fillSubrepoConfigChanges(@NotNull SubrepoConfigChangesAttributes builder,
+                                        @NotNull HgVcsRoot mainRoot,
+                                        @NotNull HgSubrepoConfigChange c) throws URISyntaxException, VcsException {
+    List<String> prevRevisions = new ArrayList<String>();
+    if (!(c.subrepoUrlChanged() || c.subrepoAdded() || c.subrepoRemoved())) {
+      String subrepoUrl = c.getCurrent().resolveUrl(mainRoot.getRepository());
+      String curRevision = c.getCurrent().revision();
+      for (SubRepo prevSubrepo : c.getPrevious()) {
+        prevRevisions.add(prevSubrepo.revision());
+      }
+      builder.addSubrepoConfigChange(new SubrepoConfigChange(mainRoot)
+              .setSubrepoPath(c.getPath())
+              .setSubrepoRootParamDiff(Constants.REPOSITORY_PROP, subrepoUrl)
+              .setCurrentSubrepoRevision(curRevision)
+              .setPreviousSubrepoRevisions(prevRevisions));
     }
   }
 
   @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));
-        }
-      }
+  private List<ModificationData> getSubrepoChanges(@NotNull OperationContext ctx, @NotNull ModificationData m) throws VcsException {
+    List<ModificationData> subrepoChanges = new ArrayList<ModificationData>();
+    for (SubrepoConfigChange configChange : SubrepoConfigChangesAttributes.readSubrepoConfigChanges(m.getAttributes())) {
+      if (configChange.getSubrepoRootParams().isEmpty())
+        continue;
+      if (configChange.getPreviousSubrepoRevisions().isEmpty())
+        continue;
+      VcsRootImpl subrepo = new VcsRootImpl(m.getVcsRootObject().getId(), configChange.getSubrepoRootParams());
+      subrepoChanges.addAll(collectChanges(ctx, subrepo, configChange.getPreviousSubrepoRevisions(), configChange.getCurrentSubrepoRevision(), CheckoutRules.DEFAULT));
     }
-    return subrepoCsets;
+    return subrepoChanges;
   }
 
   @NotNull
@@ -743,7 +744,7 @@
     return label.replace(':', '_').replace('\r', '_').replace('\n', '_');
   }
 
-  private File getWorkingDir(HgVcsRoot root) {
+  File getWorkingDir(HgVcsRoot root) {
     File customDir = root.getCustomWorkingDir();
     return customDir != null ? customDir : myMirrorManager.getMirrorDir(root.getRepository());
   }
@@ -790,6 +791,13 @@
     return myRepoFactory.create(getWorkingDir(root), myHgPathProvider.getHgPath(root), root.getAuthSettings());
   }
 
+  public ServerHgRepo createRepo(@NotNull OperationContext ctx, @NotNull HgVcsRoot root) throws VcsException {
+    ServerHgRepo repo = myRepoFactory.create(getWorkingDir(root), myHgPathProvider.getHgPath(root), root.getAuthSettings());
+    repo.setOperationContext(ctx);
+    return repo;
+  }
+
+
   private HgRepo createRepo(@NotNull HgVcsRoot root, @NotNull File customDir) throws VcsException {
     return myRepoFactory.create(customDir, myHgPathProvider.getHgPath(root), root.getAuthSettings());
   }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesCommand.java	Thu Feb 21 17:40:32 2013 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesCommand.java	Thu Feb 21 18:46:43 2013 +0400
@@ -10,16 +10,16 @@
  */
 public abstract class CollectChangesCommand {
 
-  protected String myFromRevision;
+  protected List<String> myFromRevisions;
   protected String myToRevision;
 
   @NotNull
-  public abstract List<ChangeSet> call(@NotNull String fromCommit, @NotNull String toCommit) throws VcsException;
+  public abstract List<ChangeSet> call(@NotNull List<String> fromCommits, @NotNull String toCommit) throws VcsException;
 
   public abstract List<ChangeSet> call() throws VcsException;
 
-  public CollectChangesCommand fromRevision(@NotNull String fromRevision) {
-    myFromRevision = fromRevision;
+  public CollectChangesCommand fromRevision(@NotNull List<String> fromRevisions) {
+    myFromRevisions = fromRevisions;
     return this;
   }
 
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesNoRevsets.java	Thu Feb 21 17:40:32 2013 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesNoRevsets.java	Thu Feb 21 18:46:43 2013 +0400
@@ -26,7 +26,13 @@
 
   @Override
   public List<ChangeSet> call() throws VcsException {
-    return call(myFromRevision, myToRevision);
+    return call(myFromRevisions.get(0), myToRevision);
+  }
+
+  @NotNull
+  @Override
+  public List<ChangeSet> call(@NotNull List<String> fromCommits, @NotNull String toCommit) throws VcsException {
+    return call(fromCommits.get(0), toCommit);
   }
 
   @NotNull
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesWithRevsets.java	Thu Feb 21 17:40:32 2013 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesWithRevsets.java	Thu Feb 21 18:46:43 2013 +0400
@@ -19,14 +19,20 @@
 
   @Override
   public List<ChangeSet> call() throws VcsException {
-    return call(myFromRevision, myToRevision);
+    return call(myFromRevisions, myToRevision);
   }
 
   @NotNull
-  public List<ChangeSet> call(@NotNull final String fromCommit, @NotNull final String toCommit) throws VcsException {
+  public List<ChangeSet> call(@NotNull final List<String> fromCommits, @NotNull final String toCommit) throws VcsException {
+    StringBuilder revsets = new StringBuilder();
+    revsets.append("ancestors(").append(toCommit).append(") ");
+    for (String from : fromCommits) {
+      revsets.append(" - ancestors(").append(from).append(")");
+    }
+
     return myRepo.log()
             .showCommitsFromAllBranches()
-            .withRevsets("ancestors(" + toCommit + ") - ancestors(" + fromCommit + ")")
+            .withRevsets(revsets.toString())
             .call();
   }
 }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepoTest.java	Thu Feb 21 17:40:32 2013 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepoTest.java	Thu Feb 21 18:46:43 2013 +0400
@@ -41,12 +41,12 @@
     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");
+    List<HgSubrepoConfigChange> changes = repo.getSubrepoConfigChanges("09c256b6163e");
     assertEquals(1, changes.size());
-    SubRepoConfigChange c = changes.get(0);
+    HgSubrepoConfigChange c = changes.get(0);
     assertEquals("r2", c.getPath());
     //noinspection ConstantConditions
-    assertTrue(c.getPrevious().revision().startsWith("9e4a2fef1a1c"));
+    assertTrue(c.getPrevious().get(0).revision().startsWith("9e4a2fef1a1c"));
     //noinspection ConstantConditions
     assertTrue(c.getCurrent().revision().startsWith("ebb884b1b691"));
 
@@ -55,7 +55,7 @@
     c = changes.get(0);
     assertEquals("r2", c.getPath());
     //noinspection ConstantConditions
-    assertTrue(c.getPrevious().revision().startsWith("916933c1dd8e"));
+    assertTrue(c.getPrevious().get(0).revision().startsWith("916933c1dd8e"));
     assertNull(c.getCurrent());
 
     changes = repo.getSubrepoConfigChanges("d350e7209906");
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubrepoChangesTest.java	Thu Feb 21 17:40:32 2013 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubrepoChangesTest.java	Thu Feb 21 18:46:43 2013 +0400
@@ -3,25 +3,24 @@
 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.hamcrest.Matchers;
+import jetbrains.buildServer.vcs.*;
 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.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import static java.util.Arrays.asList;
 import static jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialSupportBuilder.mercurialSupport;
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.ModificationDataMatcher.modificationData;
 import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.copyRepository;
 import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder.vcsRoot;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.startsWith;
+import static org.hamcrest.Matchers.*;
 import static org.testng.AssertJUnit.assertEquals;
 
 @Test
@@ -61,30 +60,46 @@
 
 
   public void should_report_changes_from_subrepos() throws Exception {
-    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).build();
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).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();
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).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();
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).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();
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
     List<ModificationData> changes = myVcs.collectChanges(root, "09c256b6163e", "d64d9799c143", CheckoutRules.DEFAULT);
     assertEquals(5, changes.size());
+
+    assertThat(changes, hasItem(modificationData().withVersion("d64d9799c143").withVcsRootProperties(root.getProperties())));
+
+    Map<String, String> s1props = new HashMap<String, String>(root.getProperties());
+    s1props.put("teamcity.internal.subrepo.path", "r2");
+    s1props.put(Constants.REPOSITORY_PROP, myRemoteRepo3.getCanonicalPath());
+    s1props.put("teamcity.internal.subrepo", "true");
+    assertThat(changes, hasItem(modificationData().withVersion("514c3e09cddf").withVcsRootProperties(s1props)));
+    assertThat(changes, hasItem(modificationData().withVersion("1f9eb39a3921").withVcsRootProperties(s1props)));
+
+    Map<String, String> s2props = new HashMap<String, String>(root.getProperties());
+    s2props.put("teamcity.internal.subrepo.path", "r2");
+    s2props.put(Constants.REPOSITORY_PROP, myRemoteRepo2.getCanonicalPath());
+    s2props.put("teamcity.internal.subrepo", "true");
+    assertThat(changes, hasItem(modificationData().withVersion("ac0003deae69").withVcsRootProperties(s2props)));
+    assertThat(changes, hasItem(modificationData().withVersion("29053b4b29ce").withVcsRootProperties(s2props)));
   }
 
 
@@ -95,7 +110,7 @@
             .dontUseRevsets()
             .build();
     myVcs = mercurialSupport().withConfig(pluginConfig).build();
-    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).build();
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
     List<ModificationData> changes = myVcs.collectChanges(root, "d350e7209906", "09c256b6163e", CheckoutRules.DEFAULT);
     assertEquals(3, changes.size());
   }
@@ -104,56 +119,41 @@
   public void report_subrepo_revision_changed() throws Exception {
     ServerPluginConfig pluginConfig = new ServerPluginConfigBuilder()
             .cachesDir(myTempFiles.createTempDir())
-            .reportSubrepoConfigUpdates(true)
-            .dontUseRevsets()
+            .detectSubrepoChanges(true)
             .build();
     myVcs = mercurialSupport().withConfig(pluginConfig).build();
-    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).build();
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
     List<ModificationData> changes = myVcs.collectChanges(root, "09c256b6163e", "d64d9799c143", CheckoutRules.DEFAULT);
-    assertEquals(1, changes.size());
     ModificationData m = changes.get(0);
     Map<String, String> attrs = m.getAttributes();
-    assertEquals(new File(myRemoteRepo1.getParentFile(), "r3").getCanonicalPath(), new File(attrs.get("subrepo.1.rootDiff." + Constants.REPOSITORY_PROP)).getCanonicalPath());
-    assertEquals("r2", attrs.get("subrepo.1.path"));
-    assertEquals("514c3e09cddf", attrs.get("subrepo.1.newRevision"));
-    assertEquals("ebb884b1b691", attrs.get("subrepo.1.oldRevision"));
+    List<SubrepoConfigChange> subrepoConfigChanges = SubrepoConfigChangesAttributes.readSubrepoConfigChanges(attrs);
+    assertEquals(1, subrepoConfigChanges.size());
+    SubrepoConfigChange subrepoConfigChange = subrepoConfigChanges.get(0);
+    assertThat(subrepoConfigChange.getSubrepoPath(), is("r2"));
+    assertThat(subrepoConfigChange.getCurrentSubrepoRevision(), is("514c3e09cddf"));
+    assertThat(subrepoConfigChange.getPreviousSubrepoRevisions(), is(asList("ebb884b1b691")));
   }
 
 
-  public void report_subrepo_added() throws Exception {
+  public void report_subrepo_changes() throws Exception {
     ServerPluginConfig pluginConfig = new ServerPluginConfigBuilder()
             .cachesDir(myTempFiles.createTempDir())
-            .reportSubrepoConfigUpdates(true)
+            .detectSubrepoChanges(true)
             .dontUseRevsets()
             .build();
     myVcs = mercurialSupport().withConfig(pluginConfig).build();
-    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).build();
-    List<ModificationData> changes = myVcs.collectChanges(root, "e4eced2b7381", "34017377d9c3", CheckoutRules.DEFAULT);
-    assertEquals(1, changes.size());
-    ModificationData m = changes.get(0);
-    Map<String, String> attrs = m.getAttributes();
-    assertEquals(new File(myRemoteRepo1.getParentFile(), "r2").getCanonicalPath(), new File(attrs.get("subrepo.1.rootDiff." + Constants.REPOSITORY_PROP)).getCanonicalPath());
-    assertEquals("r2", attrs.get("subrepo.1.path"));
-    assertEquals("916933c1dd8e", attrs.get("subrepo.1.newRevision"));
-    assertThat(attrs, not(Matchers.<String, String>hasKey(startsWith("subrepo.1.oldRevision"))));
+    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).withSubrepoChanges(true).build();
+    List<ModificationData> changes = myVcs.collectChanges(root, "09c256b6163e", "d64d9799c143", CheckoutRules.DEFAULT);
+    assertEquals(5, changes.size());
+
+    assertThat(changes, hasItem(modificationData().withVersion("d64d9799c143").withVcsRootProperties(root.getProperties())));
+
+    Map<String, String> subrepoRootProperties = new HashMap<String, String>(root.getProperties());
+    subrepoRootProperties.put("teamcity.internal.subrepo.path", "r2");
+    subrepoRootProperties.put(Constants.REPOSITORY_PROP, myRemoteRepo3.getCanonicalPath());
+    subrepoRootProperties.put("teamcity.internal.subrepo", "true");
+    assertThat(changes, hasItem(modificationData().withVersion("514c3e09cddf").withVcsRootProperties(subrepoRootProperties)));
+    assertThat(changes, hasItem(modificationData().withVersion("1f9eb39a3921").withVcsRootProperties(subrepoRootProperties)));
   }
 
-
-  public void report_subrepo_removed() throws Exception {
-    ServerPluginConfig pluginConfig = new ServerPluginConfigBuilder()
-            .cachesDir(myTempFiles.createTempDir())
-            .reportSubrepoConfigUpdates(true)
-            .dontUseRevsets()
-            .build();
-    myVcs = mercurialSupport().withConfig(pluginConfig).build();
-    VcsRoot root = vcsRoot().withUrl(myRemoteRepo1.getAbsolutePath()).build();
-    List<ModificationData> changes = myVcs.collectChanges(root, "34017377d9c3", "4d7b3db8779f", CheckoutRules.DEFAULT);
-    assertEquals(1, changes.size());
-    ModificationData m = changes.get(0);
-    Map<String, String> attrs = m.getAttributes();
-    assertEquals(new File(myRemoteRepo1.getParentFile(), "r2").getCanonicalPath(), new File(attrs.get("subrepo.1.rootDiff." + Constants.REPOSITORY_PROP)).getCanonicalPath());
-    assertEquals("r2", attrs.get("subrepo.1.path"));
-    assertThat(attrs, not(Matchers.<String, String>hasKey(startsWith("subrepo.1.newRevision"))));
-    assertEquals("916933c1dd8e", attrs.get("subrepo.1.oldRevision"));
-  }
 }