changeset 312:8deb526363f5

TW-17882 report changes from the merged named branches
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Thu, 15 Sep 2011 10:06:59 +0400
parents 7f45db81af21
children dd6f4af39b54
files mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesCommand.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesNoRevsets.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesWithRevsets.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandFactory.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/DagFeaturesTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/LocalRepositoryUtil.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java mercurial-tests/src/testng.xml
diffstat 9 files changed, 284 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesCommand.java	Thu Sep 15 10:06:59 2011 +0400
@@ -0,0 +1,17 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.ChangeSet;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author dmitry.neverov
+ */
+public interface CollectChangesCommand {
+
+  @NotNull
+  public List<ChangeSet> execute(@NotNull String fromCommit, @NotNull String toCommit) throws VcsException;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesNoRevsets.java	Thu Sep 15 10:06:59 2011 +0400
@@ -0,0 +1,82 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import com.intellij.openapi.util.Pair;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
+import jetbrains.buildServer.util.graph.DAG;
+import jetbrains.buildServer.util.graph.DAGIterator;
+import jetbrains.buildServer.util.graph.DAGs;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * @author dmitry.neverov
+ */
+public class CollectChangesNoRevsets implements CollectChangesCommand {
+
+  private final Settings mySettings;
+  private final File myWorkingDir;
+  private final File myTemplate;
+
+  public CollectChangesNoRevsets(@NotNull final Settings settings,
+                                 @NotNull final File workingDir,
+                                 @NotNull final File template) {
+    mySettings = settings;
+    myWorkingDir = workingDir;
+    myTemplate = template;
+  }
+
+
+  @NotNull
+  public List<ChangeSet> execute(@NotNull final String fromCommit, @NotNull final String toCommit) throws VcsException {
+    List<ChangeSet> csets = getRevisionsReachableFrom(toCommit);
+    Map<String, ChangeSet> csetsMap = getChangesetMap(csets);
+    if (csetsMap.containsKey(fromCommit)) {
+      DAG<String> dag = DAGs.createFromEdges(getEdges(csets));
+      DAGIterator<String> iter = dag.iterator(toCommit);
+      iter.markUninteresting(fromCommit);
+      List<ChangeSet> result = new ArrayList<ChangeSet>();
+      while (iter.hasNext()) {
+        String commit = iter.next();
+        ChangeSet cset = csetsMap.get(commit);
+        if (cset == null)
+          throw new IllegalStateException("Cannot find cset for id " + commit + ", csets map: " + csetsMap);
+        result.add(cset);
+      }
+      return result;
+    } else {
+      return Collections.emptyList();
+    }
+  }
+
+
+  private Map<String, ChangeSet> getChangesetMap(@NotNull final List<ChangeSet> csets) {
+    Map<String, ChangeSet> result = new HashMap<String, ChangeSet>();
+    for (ChangeSet cset : csets) {
+      result.put(cset.getId(), cset);
+    }
+    return result;
+  }
+
+
+  private List<ChangeSet> getRevisionsReachableFrom(@NotNull final String revision) throws VcsException {
+    LogCommand log = new LogCommand(mySettings, myWorkingDir, myTemplate);
+    log.setFromRevId(new ChangeSetRevision(revision).getId());
+    log.showCommitsFromAllBranches();
+    log.setToRevId("0");
+    return log.execute();
+  }
+
+
+  private List<Pair<String, String>> getEdges(List<ChangeSet> csets) {
+    List<Pair<String, String>> result = new ArrayList<Pair<String, String>>();
+    for (ChangeSet cset : csets) {
+      for (ChangeSetRevision parent : cset.getParents()) {
+        result.add(Pair.create(cset.getId(), parent.getId()));
+      }
+    }
+    return result;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesWithRevsets.java	Thu Sep 15 10:06:59 2011 +0400
@@ -0,0 +1,34 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.ChangeSet;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.LogCommand;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @author dmitry.neverov
+ */
+public class CollectChangesWithRevsets implements CollectChangesCommand {
+
+  private final Settings mySettings;
+  private final File myWorkingDir;
+  private final File myTemplate;
+
+  public CollectChangesWithRevsets(@NotNull final Settings settings, @NotNull final File workingDir, @NotNull final File template) {
+    mySettings = settings;
+    myWorkingDir = workingDir;
+    myTemplate = template;
+  }
+
+  @NotNull
+  public List<ChangeSet> execute(@NotNull final String fromCommit, @NotNull final String toCommit) throws VcsException {
+    LogCommand log = new LogCommand(mySettings, myWorkingDir, myTemplate);
+    log.showCommitsFromAllBranches();
+    log.setRevsets("ancestors(" + toCommit + ") - ancestors(" + fromCommit + ")");
+    return log.execute();
+  }
+}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandFactory.java	Wed Sep 14 18:49:59 2011 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandFactory.java	Thu Sep 15 10:06:59 2011 +0400
@@ -32,8 +32,7 @@
 
   @NotNull
   public MergeBaseCommand createMergeBase(@NotNull Settings settings, @NotNull File workingDir) throws VcsException {
-    VersionCommand versionCommand = new VersionCommand(settings, myDefaultWorkingDir);
-    HgVersion hgVersion = versionCommand.execute();
+    HgVersion hgVersion = getHgVersion(settings);
     if (hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION))
       return new MergeBaseWithRevsets(settings, workingDir, this);
     else
@@ -46,6 +45,15 @@
     return new LogCommand(settings, workingDir, myLogTemplate);
   }
 
+  @NotNull
+  public CollectChangesCommand getCollectChangesCommand(@NotNull final Settings settings, @NotNull final File workingDir) throws VcsException {
+    HgVersion hgVersion = getHgVersion(settings);
+    if (hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION)) {
+      return new CollectChangesWithRevsets(settings, workingDir, myLogTemplate);
+    } else {
+      return new CollectChangesNoRevsets(settings, workingDir, myLogTemplate);
+    }
+  }
 
   private File createLogTemplate(@NotNull final File templateFileDir) throws IOException {
     File template = new File(templateFileDir, LOG_TEMPLATE_NAME);
@@ -54,4 +62,9 @@
     }
     return template;
   }
+
+  private HgVersion getHgVersion(@NotNull final Settings settings) throws VcsException {
+    VersionCommand versionCommand = new VersionCommand(settings, myDefaultWorkingDir);
+    return versionCommand.execute();
+  }
 }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Wed Sep 14 18:49:59 2011 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Thu Sep 15 10:06:59 2011 +0400
@@ -541,25 +541,30 @@
     String toRevision = toRootRevision != null ? toRootRevision : getCurrentVersion(toRoot);
     String mergeBase = getMergeBase(settings, fromRootRevision, toRevision);
     if (mergeBase == null)
-      mergeBase = getMinusNthCommit(settings, 10);
+      return Collections.emptyList();
     return collectChanges(toRoot, mergeBase, toRootRevision, checkoutRules);
   }
 
 
   @Nullable
   private String getMergeBase(@NotNull Settings settings, @NotNull String revision1, @NotNull String revision2) throws VcsException {
-    return myCommandFactory.createMergeBase(settings, getWorkingDir(settings)).execute(revision1, revision2);
+    String result = myCommandFactory.createMergeBase(settings, getWorkingDir(settings)).execute(revision1, revision2);
+    if (result == null)
+      result = getMinusNthCommit(settings, 10);
+    return result;
   }
 
 
-  @NotNull
+  @Nullable
   private String getMinusNthCommit(@NotNull Settings settings, int n) throws VcsException {
     LogCommand log = myCommandFactory.createLog(settings, getWorkingDir(settings));
-    log.setFromRevId(settings.getBranchName());
+    log.setToRevId(settings.getBranchName());
     if (n > 0)
       log.setLimit(n);
     List<ChangeSet> changeSets = log.execute();
-    return changeSets.get(changeSets.size() - 1).getId();
+    if (changeSets.isEmpty())
+      return null;
+    return changeSets.get(0).getId();
   }
 
 
@@ -571,43 +576,49 @@
   public List<ModificationData> collectChanges(@NotNull VcsRoot root, @NotNull String fromVersion, @Nullable String currentVersion, @NotNull CheckoutRules checkoutRules) throws VcsException {
     Settings settings = createSettings(root);
     syncRepository(settings);
-
-    // first obtain changes between specified versions
     List<ModificationData> result = new ArrayList<ModificationData>();
-    if (currentVersion == null)
-      return result;
-
-    File workingDir = getWorkingDir(settings);
-    LogCommand lc = myCommandFactory.createLog(settings, workingDir);
-    String fromId = new ChangeSetRevision(fromVersion).getId();
-    lc.setFromRevId(fromId);
-    lc.setToRevId(new ChangeSetRevision(currentVersion).getId());
-    List<ChangeSet> changeSets = lc.execute();
-    if (changeSets.isEmpty()) {
-      return result;
+    for (ChangeSet cset : getChangesets(settings, fromVersion, currentVersion)) {
+      result.add(createModificationData(cset, root, checkoutRules));
     }
-
-    ChangeSet prev = new ChangeSet(fromVersion);
-    for (ChangeSet cur : changeSets) {
-      if (cur.getId().equals(fromId))
-        continue; // skip already reported changeset
-
-      List<ChangeSetRevision> curParents = cur.getParents();
-      boolean mergeCommit = curParents.size() > 1;
-      List<ModifiedFile> modifiedFiles = cur.getModifiedFiles();
-      List<VcsChange> files = toVcsChanges(modifiedFiles, prev.getFullVersion(), cur.getFullVersion(), checkoutRules);
-      if (files.isEmpty() && !mergeCommit)
-        continue;
-      ModificationData md = new ModificationData(cur.getTimestamp(), files, cur.getDescription(), cur.getUser(), root, cur.getFullVersion(), cur.getId());
-      if (mergeCommit)
-        md.setCanBeIgnored(false);
-      result.add(md);
-      prev = cur;
-    }
-
     return result;
   }
 
+
+  private ModificationData createModificationData(@NotNull final ChangeSet cset, @NotNull final VcsRoot root, @NotNull final CheckoutRules checkoutRules) {
+    List<ChangeSetRevision> parents = cset.getParents();
+    if (parents.isEmpty())
+      throw new IllegalStateException("Commit " + cset.getId() + " has no parents");
+    List<VcsChange> files = toVcsChanges(cset.getModifiedFiles(), parents.get(0).getFullVersion(), cset.getFullVersion(), checkoutRules);
+    final ModificationData result = new ModificationData(cset.getTimestamp(), files, cset.getDescription(), cset.getUser(), root, cset.getFullVersion(), cset.getId());
+    for (ChangeSetRevision parent : parents) {
+      result.addParentRevision(parent.getId());
+    }
+    if (result.getParentRevisions().size() > 1)
+      result.setCanBeIgnored(false);
+    return result;
+  }
+
+
+  @NotNull
+  private List<ChangeSet> getChangesets(@NotNull final Settings settings, @NotNull final String fromVersion, @Nullable final String toVersion) throws VcsException {
+    if (toVersion == null)
+      return Collections.emptyList();
+    String fromCommit = new ChangeSetRevision(fromVersion).getId();
+    String toCommit = new ChangeSetRevision(toVersion).getId();
+    File workingDir = getWorkingDir(settings);
+    CollectChangesCommand log = myCommandFactory.getCollectChangesCommand(settings, workingDir);
+    List<ChangeSet> changesets = log.execute(fromCommit, toCommit);
+    Iterator<ChangeSet> iter = changesets.iterator();
+    while (iter.hasNext()) {
+      ChangeSet cset = iter.next();
+      if (cset.getId().equals(fromCommit))
+        iter.remove();//skip already reported changes
+    }
+    return changesets;
+  }
+
+
+
   @NotNull
   public BuildPatchPolicy getBuildPatchPolicy() {
     return new BuildPatchByCheckoutRules() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/DagFeaturesTest.java	Thu Sep 15 10:06:59 2011 +0400
@@ -0,0 +1,76 @@
+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.impl.VcsRootImpl;
+import org.jmock.Mockery;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.util.List;
+
+import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertFalse;
+
+/**
+ * @author dmitry.neverov
+ */
+@Test
+public class DagFeaturesTest {
+
+  static {
+    Logger.setFactory(new Log4jFactory());
+  }
+
+  private TempFiles myTempFiles = new TempFiles();
+  private MercurialVcsSupport myHg;
+  private String myRepository;
+
+  @BeforeMethod
+  public void setUp() throws Exception {
+    ServerPluginConfig config = new ServerPluginConfigBuilder()
+            .cachesDir(myTempFiles.createTempDir())
+            .pluginDataDir(myTempFiles.createTempDir())
+            .build();
+    myHg = Util.createMercurialServerSupport(new Mockery(), config);
+
+    File original = new File("mercurial-tests/testData/rep2");
+    File copy = new File(myTempFiles.createTempDir(), "rep2");
+    LocalRepositoryUtil.copyRepository(original, copy);
+    myRepository = copy.getAbsolutePath();
+  }
+
+  public void tearDown() {
+    myTempFiles.cleanup();
+  }
+
+
+  //TW-17882
+  public void should_detect_changes_from_named_branches() throws Exception {
+    VcsRootImpl root = new VcsRootBuilder().repository(myRepository).hgPath("/home/nd/sandbox/3rdparty/mercurial/hg").build();
+
+    List<ModificationData> changes = myHg.collectChanges(root, "8:b6e2d176fe8e", "12:1e620196c4b6", CheckoutRules.DEFAULT);
+    assertEquals(4, changes.size());
+    for (ModificationData change : changes) {
+      assertFalse(change.getParentRevisions().isEmpty());
+    }
+
+    changes = myHg.collectChanges(root, "12:1e620196c4b6", "18:df04faa7575a", CheckoutRules.DEFAULT);
+    assertEquals(6, changes.size());
+    for (ModificationData change : changes) {
+      assertFalse(change.getParentRevisions().isEmpty());
+    }
+  }
+
+
+  //TW-17882
+  public void should_report_changes_only_from_merged_named_branches() throws Exception {
+    VcsRootImpl root = new VcsRootBuilder().repository(myRepository).hgPath("/home/nd/sandbox/3rdparty/mercurial/hg").build();
+    List<ModificationData> changes = myHg.collectChanges(root, "1e620196c4b6", "505c5b9d01e6", CheckoutRules.DEFAULT);
+    assertEquals(2, changes.size());
+  }
+}
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/LocalRepositoryUtil.java	Wed Sep 14 18:49:59 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/LocalRepositoryUtil.java	Thu Sep 15 10:06:59 2011 +0400
@@ -43,10 +43,7 @@
     File repository = myRepositories.get(repPath);
     if (repository != null) return repository;
     final File tempDir = myTempFiles.createTempDir();
-    FileUtil.copyDir(new File(repPath), tempDir);
-    if (new File(tempDir, "hg").isDirectory()) {
-      FileUtil.rename(new File(tempDir, "hg"), new File(tempDir, ".hg"));
-    }
+    copyRepository(new File(repPath), tempDir);
     myRepositories.put(repPath, tempDir);
     return tempDir;
   }
@@ -54,4 +51,11 @@
   public static void forgetRepository(@NotNull String repPath) {
     myRepositories.remove(repPath);
   }
+
+
+  public static void copyRepository(File src, File dst) throws IOException {
+    FileUtil.copyDir(src, dst);
+    if (new File(dst, "hg").isDirectory())
+      FileUtil.rename(new File(dst, "hg"), new File(dst, ".hg"));
+  }
 }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Wed Sep 14 18:49:59 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Thu Sep 15 10:06:59 2011 +0400
@@ -398,10 +398,10 @@
     VcsRootImpl vcsRoot = createVcsRoot(mergeCommittsRepo());
 
     List<ModificationData> changes = collectChanges(vcsRoot, "8:b6e2d176fe8e", "12:1e620196c4b6", CheckoutRules.DEFAULT);
-    assertEquals(changes.size(), 2);
+    assertEquals(changes.size(), 4);
 
-    assertFiles(Arrays.asList("A dir6/file6.txt"), changes.get(0));
-    assertFiles(Arrays.asList("M dir6/file6.txt", "A dir5/file5.txt"), changes.get(1));
+    assertFiles(Arrays.asList("A dir6/file6.txt"), changes.get(2));
+    assertFiles(Arrays.asList("M dir6/file6.txt", "A dir5/file5.txt"), changes.get(3));
   }
 
   //TW-17530
--- a/mercurial-tests/src/testng.xml	Wed Sep 14 18:49:59 2011 +0400
+++ b/mercurial-tests/src/testng.xml	Thu Sep 15 10:06:59 2011 +0400
@@ -16,6 +16,7 @@
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.HgVersionTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.command.VersionCommandTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerHgPathProviderTest"/>
+      <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.DagFeaturesTest"/>
     </classes>
   </test>
 </suite>