changeset 90:5d9c34cb543a Darjeeling-5.0.x

improving merge commits reporting
author Pavel.Sher
date Wed, 09 Dec 2009 01:28:10 +0300
parents 05e962a88f6f
children 99fc78c3e8db
files mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangeSet.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangedFilesCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ModifiedFile.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommand.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java mercurial-tests/testData/rep1/hg/branch mercurial-tests/testData/rep1/hg/branchheads.cache mercurial-tests/testData/rep1/hg/dirstate mercurial-tests/testData/rep1/hg/store/00changelog.i mercurial-tests/testData/rep1/hg/store/00manifest.i mercurial-tests/testData/rep1/hg/store/data/file.txt.i mercurial-tests/testData/rep1/hg/store/undo mercurial-tests/testData/rep1/hg/tags.cache mercurial-tests/testData/rep1/hg/undo.dirstate mercurial-tests/testData/rep2/hg/branch mercurial-tests/testData/rep2/hg/branchheads.cache mercurial-tests/testData/rep2/hg/dirstate mercurial-tests/testData/rep2/hg/store/00changelog.i mercurial-tests/testData/rep2/hg/store/00manifest.i mercurial-tests/testData/rep2/hg/store/data/dir4/file4.txt.i mercurial-tests/testData/rep2/hg/store/data/dir4/file41.txt.i mercurial-tests/testData/rep2/hg/store/data/dir4/file42.txt.i mercurial-tests/testData/rep2/hg/store/data/dir4/file43.txt.i mercurial-tests/testData/rep2/hg/store/fncache mercurial-tests/testData/rep2/hg/store/undo mercurial-tests/testData/rep2/hg/tags.cache mercurial-tests/testData/rep2/hg/undo.dirstate
diffstat 32 files changed, 275 insertions(+), 69 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangeSet.java	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangeSet.java	Wed Dec 09 01:28:10 2009 +0300
@@ -28,7 +28,8 @@
 public class ChangeSet extends ChangeSetRevision {
   @NotNull private String myUser;
   @NotNull private Date myTimestamp;
-  private String mySummary;
+  private String myDescription;
+  private boolean myContainsFiles;
   private List<ChangeSetRevision> myParents;
 
   public ChangeSet(final int revNumber, @NotNull final String id) {
@@ -51,8 +52,12 @@
     myTimestamp = timestamp;
   }
 
-  public void setSummary(final String summary) {
-    mySummary = summary;
+  public void setDescription(final String description) {
+    myDescription = description;
+  }
+
+  public void setContainsFiles(final boolean containsFiles) {
+    myContainsFiles = containsFiles;
   }
 
   public void addParent(@NotNull ChangeSetRevision rev) {
@@ -84,8 +89,8 @@
    * Returns changeset summary specified by user
    * @return changeset summary
    */
-  public String getSummary() {
-    return mySummary;
+  public String getDescription() {
+    return myDescription;
   }
 
   /**
@@ -96,4 +101,12 @@
   public List<ChangeSetRevision> getParents() {
     return myParents;
   }
+
+  /**
+   * Returns true if this change has changed files
+   * @return see above
+   */
+  public boolean containsFiles() {
+    return myContainsFiles;
+  }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangedFilesCommand.java	Wed Dec 09 01:28:10 2009 +0300
@@ -0,0 +1,61 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import com.intellij.execution.configurations.GeneralCommandLine;
+import jetbrains.buildServer.ExecResult;
+import jetbrains.buildServer.util.FileUtil;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Pavel.Sher
+ */
+public class ChangedFilesCommand extends BaseCommand {
+  private String myRevId;
+
+  public ChangedFilesCommand(@NotNull final Settings settings) {
+    super(settings);
+  }
+
+  public void setRevId(@NotNull final String revId) {
+    myRevId = revId;
+  }
+
+  public List<ModifiedFile> execute() throws VcsException {
+    File styleFile;
+    try {
+      styleFile = getStyleFile();
+    } catch (IOException e) {
+      throw new VcsException("Unable to create style file: " + e.toString(), e);
+    }
+    try {
+      GeneralCommandLine cli = createCommandLine();
+      cli.addParameter("log");
+      cli.addParameter("-r");
+      cli.addParameter(myRevId + ":" + myRevId);
+      cli.addParameter("--style=" + styleFile.getAbsolutePath());
+
+      ExecResult res = runCommand(cli);
+      return parseFiles(res.getStdout());
+    } finally {
+      FileUtil.delete(styleFile);
+    }
+  }
+
+  private File getStyleFile() throws IOException {
+    File styleFile = FileUtil.createTempFile("hg", "style");
+    FileUtil.writeFile(styleFile,
+            "changeset = \"{file_mods}{file_adds}{file_dels}\"\n" +
+            "file_add = \"A {file_add}\\n\"\n" +
+            "file_del = \"R {file_del}\\n\"\n" +
+            "file_mod = \"M {file_mod}\\n\"");
+    return styleFile;
+  }
+
+  private List<ModifiedFile> parseFiles(final String stdout) {
+    return StatusCommand.parseFiles(stdout);
+  }
+}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Wed Dec 09 01:28:10 2009 +0300
@@ -38,7 +38,8 @@
   private static final String PARENT_PREFIX = "parent:";
   private static final String DATE_PREFIX = "date:";
   private static final String DATE_FORMAT = "EEE MMM d HH:mm:ss yyyy Z";
-  private static final String SUMMARY_PREFIX = "summary:";
+  private static final String DESCRIPTION_PREFIX = "description:";
+  private static final String FILES_PREFIX = "files:";
 
   public LogCommand(@NotNull Settings settings) {
     super(settings);
@@ -59,6 +60,7 @@
   public List<ChangeSet> execute() throws VcsException {
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("log");
+    cli.addParameter("-v");
     cli.addParameter("-b");
     cli.addParameter(getSettings().getBranchName());
     cli.addParameter("-r");
@@ -81,12 +83,20 @@
     List<ChangeSet> result = new ArrayList<ChangeSet>();
     String[] lines = stdout.split("\n");
     ChangeSet current = null;
-    int lineNum = 0;                  
+    int lineNum = 0;
+    boolean insideDescription = false;
+    StringBuilder descr = new StringBuilder();
     while (lineNum < lines.length) {
       String line = lines[lineNum];
       lineNum++;
 
       if (line.startsWith(CHANGESET_PREFIX)) {
+        insideDescription = false;
+        if (current != null) {
+          current.setDescription(descr.toString().trim());
+          descr.setLength(0);
+        }
+
         String revAndId = line.substring(CHANGESET_PREFIX.length()).trim();
         try {
           current = new ChangeSet(revAndId);
@@ -105,6 +115,11 @@
         continue;
       }
 
+      if (line.startsWith(FILES_PREFIX)) {
+        current.setContainsFiles(true);
+        continue;
+      }
+
       if (line.startsWith(PARENT_PREFIX)) {
         String parentRev = line.substring(PARENT_PREFIX.length()).trim();
         current.addParent(new ChangeSetRevision(parentRev));
@@ -124,12 +139,18 @@
         continue;
       }
 
-      if (line.startsWith(SUMMARY_PREFIX)) {
-        String summary = line.substring(SUMMARY_PREFIX.length()).trim();
-        current.setSummary(summary);
-
+      if (line.startsWith(DESCRIPTION_PREFIX)) {
+        insideDescription = true;
         continue;
       }
+
+      if (insideDescription) {
+        descr.append(line).append("\n");
+      }
+    }
+
+    if (insideDescription) {
+      current.setDescription(descr.toString().trim());
     }
 
     return result;
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ModifiedFile.java	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ModifiedFile.java	Wed Dec 09 01:28:10 2009 +0300
@@ -66,4 +66,21 @@
   public String getPath() {
     return myPath;
   }
+
+  @Override
+  public boolean equals(final Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    final ModifiedFile that = (ModifiedFile) o;
+
+    return myPath.equals(that.myPath) && myStatus == that.myStatus;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = myStatus.hashCode();
+    result = 31 * result + myPath.hashCode();
+    return result;
+  }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommand.java	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommand.java	Wed Dec 09 01:28:10 2009 +0300
@@ -52,7 +52,7 @@
     return parseFiles(res.getStdout());
   }
 
-  private List<ModifiedFile> parseFiles(final String stdout) {
+  public static List<ModifiedFile> parseFiles(final String stdout) {
     List<ModifiedFile> result = new ArrayList<ModifiedFile>();
     String[] lines = stdout.split("\n");
     for (String line: lines) {
@@ -66,7 +66,7 @@
     return result;
   }
 
-  private ModifiedFile.Status toStatus(final char modifier) {
+  public static ModifiedFile.Status toStatus(final char modifier) {
     switch (modifier) {
       case 'A': return ModifiedFile.Status.ADDED;
       case 'M': return ModifiedFile.Status.MODIFIED;
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Wed Dec 09 01:28:10 2009 +0300
@@ -103,14 +103,30 @@
     for (ChangeSet cur : changeSets) {
       if (cur.getId().equals(fromId)) continue; // skip already reported changeset
 
-      st.setFromRevId(prev.getId());
-      st.setToRevId(cur.getId());
-      List<ModifiedFile> modifiedFiles = st.execute();
+      String prevId = prev.getId();
+      List<ChangeSetRevision> curParents = cur.getParents();
+      boolean merge = curParents != null && curParents.size() > 1;
+      if (curParents != null && !merge) {
+        prevId = curParents.get(0).getId();
+      }
+
+      List<ModifiedFile> modifiedFiles = new ArrayList<ModifiedFile>();
+      if (merge) {
+        modifiedFiles.addAll(computeModifiedFilesForMergeCommit(settings, cur));
+      } else {
+        st.setFromRevId(prevId);
+        st.setToRevId(cur.getId());
+        modifiedFiles = st.execute();
+      }
+
       // changeset full version will be set into VcsChange structure and
       // stored in database (note that getContent method will be invoked with this version)
       List<VcsChange> files = toVcsChanges(modifiedFiles, prev.getFullVersion(), cur.getFullVersion(), includeRule);
-      if (files.isEmpty()) continue;
-      ModificationData md = new ModificationData(cur.getTimestamp(), files, cur.getSummary(), cur.getUser(), root, cur.getFullVersion(), cur.getId());
+      if (files.isEmpty() && !merge) continue;
+      ModificationData md = new ModificationData(cur.getTimestamp(), files, cur.getDescription(), cur.getUser(), root, cur.getFullVersion(), cur.getId());
+      if (merge) {
+        md.setCanBeIgnored(false);
+      }
       result.add(md);
       prev = cur;
     }
@@ -118,6 +134,14 @@
     return result;
   }
 
+  private Collection<ModifiedFile> computeModifiedFilesForMergeCommit(final Settings settings, final ChangeSet cur) throws VcsException {
+    if (!cur.containsFiles()) return Collections.emptyList();
+
+    ChangedFilesCommand cfc = new ChangedFilesCommand(settings);
+    cfc.setRevId(cur.getId());
+    return cfc.execute();
+  }
+
   private List<VcsChange> toVcsChanges(final List<ModifiedFile> modifiedFiles, String prevVer, String curVer, final IncludeRule includeRule) {
     List<VcsChange> files = new ArrayList<VcsChange>();
     for (ModifiedFile mf: modifiedFiles) {
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java	Wed Dec 09 01:28:10 2009 +0300
@@ -34,11 +34,11 @@
   }
 
   public void checkout_on_agent() throws IOException, VcsException {
-    testUpdate(createVcsRoot(), "4:b06a290a363b", "cleanPatch1/after", new IncludeRule(".", ".", null));
+    testUpdate(createVcsRoot(simpleRepo()), "4:b06a290a363b", "cleanPatch1/after", new IncludeRule(".", ".", null));
   }
 
   public void checkout_on_agent_include_rule_with_mapping() throws IOException, VcsException {
-    testUpdate(createVcsRoot(), "4:b06a290a363b", "cleanPatch1/after", new IncludeRule("+:.", "subdir", null));
+    testUpdate(createVcsRoot(simpleRepo()), "4:b06a290a363b", "cleanPatch1/after", new IncludeRule("+:.", "subdir", null));
   }
 
   private void testUpdate(final VcsRoot vcsRoot, String version, String expected, final IncludeRule includeRule) throws VcsException, IOException {
@@ -62,11 +62,11 @@
   }
 
   public void checkout_on_agent_from_branch() throws IOException, VcsException {
-    testUpdate(createVcsRoot("test_branch"), "7:376dcf05cd2a", "patch3/after", new IncludeRule(".", ".", null));
+    testUpdate(createVcsRoot(simpleRepo(), "test_branch"), "7:376dcf05cd2a", "patch3/after", new IncludeRule(".", ".", null));
   }
 
   public void update_on_agent() throws IOException, VcsException {
-    VcsRoot vcsRoot = createVcsRoot();
+    VcsRoot vcsRoot = createVcsRoot(simpleRepo());
     doUpdate(vcsRoot, "3:9522278aa38d", new IncludeRule(".", ".", null));
     File workDir = doUpdate(vcsRoot, "4:b06a290a363b", new IncludeRule(".", ".", null));
 
@@ -74,7 +74,7 @@
   }
 
   public void update_on_agent_with_include_rule() throws IOException, VcsException {
-    VcsRoot vcsRoot = createVcsRoot();
+    VcsRoot vcsRoot = createVcsRoot(simpleRepo());
     doUpdate(vcsRoot, "3:9522278aa38d", new IncludeRule(".", "subdir", null));
     File workDir = doUpdate(vcsRoot, "4:b06a290a363b", new IncludeRule(".", "subdir", null));
 
@@ -82,7 +82,7 @@
   }
 
   public void update_on_agent_from_branch() throws IOException, VcsException {
-    VcsRoot vcsRoot = createVcsRoot("test_branch");
+    VcsRoot vcsRoot = createVcsRoot(simpleRepo(), "test_branch");
     doUpdate(vcsRoot, "7:376dcf05cd2a", new IncludeRule(".", ".", null));
     File workDir = doUpdate(vcsRoot, "8:04c3ae4c6312", new IncludeRule(".", ".", null));
 
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java	Wed Dec 09 01:28:10 2009 +0300
@@ -35,26 +35,25 @@
     myTempFiles.cleanup();
   }
 
-  protected VcsRootImpl createVcsRoot() throws IOException {
+  protected VcsRootImpl createVcsRoot(@NotNull String repPath) throws IOException {
     VcsRootImpl vcsRoot = new VcsRootImpl(1, Constants.VCS_NAME);
     vcsRoot.addProperty(Constants.HG_COMMAND_PATH_PROP, new File("mercurial-tests/testData/bin/hg.exe").getAbsolutePath());
-    String repPath = getRepositoryPath();
     File repository = LocalRepositoryUtil.prepareRepository(repPath);
     vcsRoot.addProperty(Constants.REPOSITORY_PROP, repository.getAbsolutePath());
     return vcsRoot;
   }
 
-  protected VcsRootImpl createVcsRoot(@NotNull String branchName) throws IOException {
-    VcsRootImpl vcsRoot = createVcsRoot();
+  protected VcsRootImpl createVcsRoot(@NotNull String repPath, @NotNull String branchName) throws IOException {
+    VcsRootImpl vcsRoot = createVcsRoot(repPath);
     vcsRoot.addProperty(Constants.BRANCH_NAME_PROP, branchName);
     return vcsRoot;
   }
 
-  private String getRepositoryPath() {
-    return new File("mercurial-tests/testData/rep1").getAbsolutePath();
+  protected void cleanRepositoryAfterTest(@NotNull String repPath) {
+    LocalRepositoryUtil.forgetRepository(repPath);
   }
 
-  protected void cleanRepositoryAfterTest() {
-    LocalRepositoryUtil.forgetRepository(getRepositoryPath());
+  protected String simpleRepo() {
+    return new File("mercurial-tests/testData/rep1").getAbsolutePath();
   }
 }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Wed Dec 09 01:28:10 2009 +0300
@@ -24,6 +24,7 @@
 import jetbrains.buildServer.vcs.*;
 import jetbrains.buildServer.vcs.impl.VcsRootImpl;
 import jetbrains.buildServer.vcs.patches.PatchBuilderImpl;
+import junit.framework.Assert;
 import org.jmock.Mock;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -32,6 +33,8 @@
 import java.io.File;
 import java.io.FilenameFilter;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -62,18 +65,18 @@
   }
 
   public void test_get_current_version() throws Exception {
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
 
-    assertEquals(myVcs.getCurrentVersion(vcsRoot), "6:b9deb9a1c6f4");
-    assertEquals("b9deb9a1c6f4", myVcs.getVersionDisplayName("6:b9deb9a1c6f4", vcsRoot));
+    assertEquals(myVcs.getCurrentVersion(vcsRoot), "10:9c6a6b4aede0");
+    assertEquals("9c6a6b4aede0", myVcs.getVersionDisplayName("10:9c6a6b4aede0", vcsRoot));
 
-    assertEquals(myVcs.getCurrentVersion(createVcsRoot("test_branch")), "8:04c3ae4c6312");
+    assertEquals(myVcs.getCurrentVersion(createVcsRoot(simpleRepo(), "test_branch")), "8:04c3ae4c6312");
 
-    assertEquals(myVcs.getCurrentVersion(createVcsRoot("name with space")), "9:9babcf2d5705");
+    assertEquals(myVcs.getCurrentVersion(createVcsRoot(simpleRepo(), "name with space")), "9:9babcf2d5705");
   }
 
   public void test_collect_changes() throws Exception {
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
 
     List<ModificationData> changes = myVcs.collectBuildChanges(vcsRoot, "0:9875b412a788", "3:9522278aa38d", new CheckoutRules(""));
     assertEquals(3, changes.size());
@@ -104,7 +107,7 @@
   }
 
   public void test_collect_changes_with_checkout_rules() throws Exception {
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
 
     List<ModificationData> changes = myVcs.collectBuildChanges(vcsRoot, "0:9875b412a788", "3:9522278aa38d", new CheckoutRules("-:.\n+:dir1/subdir"));
     assertEquals(changes.size(), 0);
@@ -118,7 +121,7 @@
   @Test
   public void test_build_patch() throws IOException, VcsException {
     setName("cleanPatch1");
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
 
     final ByteArrayOutputStream output = new ByteArrayOutputStream();
     final PatchBuilderImpl builder = new PatchBuilderImpl(output);
@@ -139,7 +142,7 @@
 
   public void test_build_incremental_patch() throws IOException, VcsException {
     setName("patch1");
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
 
     final ByteArrayOutputStream output = new ByteArrayOutputStream();
     final PatchBuilderImpl builder = new PatchBuilderImpl(output);
@@ -152,7 +155,7 @@
 
   public void test_build_incremental_patch_checkout_rules() throws IOException, VcsException {
     setName("patch1_checkout_rules");
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
 
     final ByteArrayOutputStream output = new ByteArrayOutputStream();
     final PatchBuilderImpl builder = new PatchBuilderImpl(output);
@@ -165,7 +168,7 @@
 
   public void test_build_incremental_patch_file_with_space() throws IOException, VcsException {
     setName("patch2");
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
 
     final ByteArrayOutputStream output = new ByteArrayOutputStream();
     final PatchBuilderImpl builder = new PatchBuilderImpl(output);
@@ -177,7 +180,7 @@
   }
 
   public void test_get_content() throws IOException, VcsException {
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
 
     byte[] content = myVcs.getContent("dir1/subdir/file2.txt", vcsRoot, "4:b06a290a363b");
     assertEquals(new String(content), "bbb");
@@ -186,15 +189,14 @@
   }
 
   public void test_get_content_in_branch() throws IOException, VcsException {
-    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo(), "test_branch");
 
-    byte[] content = myVcs.getContent("file_in_branch.txt", vcsRoot, "7:376dcf05cd2a");
-    content = myVcs.getContent("file_in_branch.txt", vcsRoot, "8:04c3ae4c6312");
+    byte[] content = myVcs.getContent("file_in_branch.txt", vcsRoot, "8:04c3ae4c6312");
     assertEquals(new String(content), "file from the test_branch\r\nfile modified");
   }
 
   public void test_test_connection() throws IOException, VcsException {
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
 
     System.out.println(myVcs.testConnection(vcsRoot));
 
@@ -207,8 +209,8 @@
   }
 
   public void test_tag() throws IOException, VcsException {
-    VcsRootImpl vcsRoot = createVcsRoot();
-    cleanRepositoryAfterTest(); 
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
+    cleanRepositoryAfterTest(simpleRepo());
 
     String actualTag = myVcs.label("new:tag", "1:1d446e82d356", vcsRoot, new CheckoutRules(""));
     assertEquals(actualTag, "new_tag");
@@ -224,8 +226,8 @@
   }
 
   public void test_tag_in_branch() throws IOException, VcsException {
-    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
-    cleanRepositoryAfterTest();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo(), "test_branch");
+    cleanRepositoryAfterTest(simpleRepo());
 
     String actualTag = myVcs.label("branch_tag", "7:376dcf05cd2a", vcsRoot, new CheckoutRules(""));
     assertEquals(actualTag, "branch_tag");
@@ -241,7 +243,7 @@
   }
 
   public void test_collect_changes_in_branch() throws Exception {
-    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo(), "test_branch");
 
     // fromVersion(6:b9deb9a1c6f4) is not in the branch (it is in the default branch)
     List<ModificationData> changes = myVcs.collectBuildChanges(vcsRoot, "6:b9deb9a1c6f4", "7:376dcf05cd2a", new CheckoutRules(""));
@@ -264,7 +266,7 @@
   }
 
   public void test_collect_changes_after_merge() throws IOException, VcsException {
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
 
     List<ModificationData> changes = myVcs.collectBuildChanges(vcsRoot, "6:b9deb9a1c6f4", "12:1870e1100fab", new CheckoutRules(""));
     for (ModificationData md: changes) {
@@ -274,7 +276,7 @@
 
   public void test_full_patch_from_branch() throws IOException, VcsException {
     setName("patch3");
-    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo(), "test_branch");
 
     final ByteArrayOutputStream output = new ByteArrayOutputStream();
     final PatchBuilderImpl builder = new PatchBuilderImpl(output);
@@ -287,7 +289,7 @@
 
   public void test_full_patch_from_branch_with_checkout_rules() throws IOException, VcsException {
     setName("patch3_checkout_rules1");
-    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo(), "test_branch");
 
     final ByteArrayOutputStream output = new ByteArrayOutputStream();
     final PatchBuilderImpl builder = new PatchBuilderImpl(output);
@@ -300,7 +302,7 @@
 
   public void test_full_patch_from_branch_with_checkout_rules_mapped_and_skipped() throws IOException, VcsException {
     setName("patch3_checkout_rules2");
-    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo(), "test_branch");
 
     final ByteArrayOutputStream output = new ByteArrayOutputStream();
     final PatchBuilderImpl builder = new PatchBuilderImpl(output);
@@ -313,7 +315,7 @@
 
   public void test_incremental_patch_from_branch() throws IOException, VcsException {
     setName("patch4");
-    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo(), "test_branch");
 
     final ByteArrayOutputStream output = new ByteArrayOutputStream();
     final PatchBuilderImpl builder = new PatchBuilderImpl(output);
@@ -326,7 +328,7 @@
 
   @Test(enabled = false)
   public void support_anchor_branch_notation() throws IOException {
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
     String repPath = vcsRoot.getProperty(Constants.REPOSITORY_PROP);
     vcsRoot.addProperty(Constants.REPOSITORY_PROP, repPath + "#test_branch");
     Settings settings = new Settings(new File(myServerPaths.getCachesDir()), vcsRoot);
@@ -343,7 +345,7 @@
 
   public void build_patch_using_custom_clone_path() throws IOException, VcsException {
     setName("cleanPatch1");
-    VcsRootImpl vcsRoot = createVcsRoot();
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
     File cloneDir = myTempFiles.createTempDir();
     vcsRoot.addProperty(Constants.SERVER_CLONE_PATH_PROP, cloneDir.getAbsolutePath());
 
@@ -358,6 +360,55 @@
     assertTrue(new File(cloneDir, new File(vcsRoot.getProperty(Constants.REPOSITORY_PROP)).getName()).isDirectory());
   }
 
+  private String mergeCommittsRepo() {
+    return new File("mercurial-tests/testData/rep2").getAbsolutePath();
+  }
+
+  public void test_collect_changes_in_repo_with_merge_commits() throws Exception {
+    VcsRootImpl vcsRoot = createVcsRoot(mergeCommittsRepo());
+
+    List<ModificationData> changes = myVcs.collectBuildChanges(vcsRoot, "1:a3d15477d297", "4:6eeb8974fe67", new CheckoutRules(""));
+    assertEquals(changes.size(), 3);
+
+    assertEquals("2:db8a04d262f3", changes.get(0).getVersion());
+    assertEquals("3:2538c02bafeb", changes.get(1).getVersion());
+    assertEquals("4:6eeb8974fe67", changes.get(2).getVersion());
+
+    assertFiles(Arrays.asList("A dir1/file1.txt"), changes.get(0));
+    assertFiles(Arrays.asList("A dir2/file2.txt"), changes.get(1));
+    Assert.assertEquals(changes.get(2).getChanges().toString(), 0, changes.get(2).getChangeCount());
+  }
+
+  public void test_collect_changes_in_repo_with_merge_commits_conflict() throws Exception {
+    VcsRootImpl vcsRoot = createVcsRoot(mergeCommittsRepo());
+
+    List<ModificationData> changes = myVcs.collectBuildChanges(vcsRoot, "6:6066b677d026", "8:b6e2d176fe8e", new CheckoutRules(""));
+    assertEquals(changes.size(), 2);
+
+    assertFiles(Arrays.asList("A dir4/file41.txt"), changes.get(0));
+    assertFiles(Arrays.asList("M dir4/file41.txt", "A dir4/file42.txt", "A dir4/file43.txt", "R dir3/file3.txt"), changes.get(1));
+  }
+
+  private void assertFiles(final List<String> expectedFiles, final ModificationData modificationData) {
+    List<String> actualFiles = new ArrayList<String>();
+    for (VcsChange vc: modificationData.getChanges()) {
+      actualFiles.add(toFileStatus(vc.getType()) + " " + vc.getRelativeFileName());
+    }
+    Assert.assertEquals("Actual files: " + actualFiles.toString(), expectedFiles, actualFiles);
+  }
+
+  private String toFileStatus(VcsChange.Type type) {
+    switch (type) {
+      case ADDED:
+        return "A";
+      case REMOVED:
+        return "R";
+      case CHANGED:
+        return "M";
+    }
+    return "?";
+  }
+
   private Object normalizePath(final String path) {
     return path.replace(File.separatorChar, '/');
   }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java	Wed Dec 09 01:28:10 2009 +0300
@@ -40,7 +40,7 @@
     assertEquals(0, changeSet.getRevNumber());
     assertEquals(toId, changeSet.getId());
     assertEquals("pavel@localhost", changeSet.getUser());
-    assertEquals("dir1 created", changeSet.getSummary());
+    assertEquals("dir1 created", changeSet.getDescription());
     assertNull(changeSet.getParents());
   }
 
@@ -52,14 +52,14 @@
     ChangeSet changeSet1 = changes.get(0);
     final ChangeSet changeSet2 = changes.get(1);
     final ChangeSet changeSet3 = changes.get(2);
-    assertEquals("dir1 created", changeSet1.getSummary());
-    assertEquals("new file added", changeSet2.getSummary());
-    assertEquals("file4.txt added", changeSet3.getSummary());
+    assertEquals("dir1 created", changeSet1.getDescription());
+    assertEquals("new file added", changeSet2.getDescription());
+    assertEquals("file4.txt added", changeSet3.getDescription());
 
     changes = runLog(null, toId);
     assertEquals(3, changes.size());
     changeSet1 = changes.get(2);
-    assertEquals("file4.txt added", changeSet1.getSummary());
+    assertEquals("file4.txt added", changeSet1.getDescription());
   }
 
   public void changeset_parents() throws VcsException, IOException {
@@ -73,6 +73,16 @@
     assertTrue(cs.getParents().contains(new ChangeSetRevision("3:2538c02bafeb")));
   }
 
+  public void parse_multiline_description() throws VcsException, IOException {
+    List<ChangeSet> changes = runLog("9babcf2d5705", "9c6a6b4aede0");
+    assertEquals(1, changes.size());
+    assertEquals("Multiline description\n" +
+            "description with new\n" +
+            "lines\n" +
+            "aaaa\n" +
+            "bbb", changes.get(0).getDescription());
+  }
+
   private List<ChangeSet> runLog(final String fromId, final String toId) throws IOException, VcsException {
     return runCommand(new CommandExecutor<List<ChangeSet>>() {
       public List<ChangeSet> execute(@NotNull final Settings settings) throws VcsException {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/rep1/hg/branch	Wed Dec 09 01:28:10 2009 +0300
@@ -0,0 +1,1 @@
+default
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/rep1/hg/branchheads.cache	Wed Dec 09 01:28:10 2009 +0300
@@ -0,0 +1,4 @@
+9babcf2d5705e64e85375516be4ede87baf9ce82 9
+b9deb9a1c6f43e8a3b9f7d12c1ec468352b4397e default
+04c3ae4c631244646c017d503be49fe62e9aea9c test_branch
+9babcf2d5705e64e85375516be4ede87baf9ce82 name with space
Binary file mercurial-tests/testData/rep1/hg/dirstate has changed
Binary file mercurial-tests/testData/rep1/hg/store/00changelog.i has changed
Binary file mercurial-tests/testData/rep1/hg/store/00manifest.i has changed
Binary file mercurial-tests/testData/rep1/hg/store/data/file.txt.i has changed
Binary file mercurial-tests/testData/rep1/hg/store/undo has changed
--- a/mercurial-tests/testData/rep1/hg/tags.cache	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-tests/testData/rep1/hg/tags.cache	Wed Dec 09 01:28:10 2009 +0300
@@ -1,2 +1,3 @@
+10 9c6a6b4aede0c48cd8b9942bbd932a8349145c67
 9 9babcf2d5705e64e85375516be4ede87baf9ce82
 
Binary file mercurial-tests/testData/rep1/hg/undo.dirstate has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/rep2/hg/branch	Wed Dec 09 01:28:10 2009 +0300
@@ -0,0 +1,1 @@
+default
--- a/mercurial-tests/testData/rep2/hg/branchheads.cache	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-tests/testData/rep2/hg/branchheads.cache	Wed Dec 09 01:28:10 2009 +0300
@@ -1,3 +1,2 @@
-2538c02bafeba83f2847b4eaf1c2d9f4f0ca891d 3
-a3d15477d297a34f0f4750bd959c24ec1dce5c81 default
-2538c02bafeba83f2847b4eaf1c2d9f4f0ca891d default
+0f306546fb24b3e7332b8a5547af8d5006eb3105 8
+0f306546fb24b3e7332b8a5547af8d5006eb3105 default
Binary file mercurial-tests/testData/rep2/hg/dirstate has changed
Binary file mercurial-tests/testData/rep2/hg/store/00changelog.i has changed
Binary file mercurial-tests/testData/rep2/hg/store/00manifest.i has changed
Binary file mercurial-tests/testData/rep2/hg/store/data/dir4/file4.txt.i has changed
Binary file mercurial-tests/testData/rep2/hg/store/data/dir4/file41.txt.i has changed
Binary file mercurial-tests/testData/rep2/hg/store/data/dir4/file42.txt.i has changed
Binary file mercurial-tests/testData/rep2/hg/store/data/dir4/file43.txt.i has changed
--- a/mercurial-tests/testData/rep2/hg/store/fncache	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-tests/testData/rep2/hg/store/fncache	Wed Dec 09 01:28:10 2009 +0300
@@ -2,3 +2,7 @@
 data/dir3/file3.txt.i
 data/dir1/file1.txt.i
 data/dir2/file2.txt.i
+data/dir4/file4.txt.i
+data/dir4/file41.txt.i
+data/dir4/file42.txt.i
+data/dir4/file43.txt.i
Binary file mercurial-tests/testData/rep2/hg/store/undo has changed
--- a/mercurial-tests/testData/rep2/hg/tags.cache	Tue Dec 08 20:21:08 2009 +0300
+++ b/mercurial-tests/testData/rep2/hg/tags.cache	Wed Dec 09 01:28:10 2009 +0300
@@ -1,2 +1,2 @@
-4 6eeb8974fe671e094bde9dc7d787a24440587afe
+8 b6e2d176fe8e13ce07bc915041453104f1559b7a
 
Binary file mercurial-tests/testData/rep2/hg/undo.dirstate has changed