changeset 289:86ae99a7c5d6 Eluru-6.5.x

Use customized xml output from the 'hg log' command instead of running 'hg status' for every commit
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Wed, 07 Sep 2011 12:40:50 +0400
parents 58dfe58f2b8b
children fea86366d792
files mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangeSet.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandUtil.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java mercurial-server/resources/buildServerResources/log.template mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java mercurial.ipr
diffstat 8 files changed, 236 insertions(+), 156 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangeSet.java	Wed Sep 07 11:58:54 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangeSet.java	Wed Sep 07 12:40:50 2011 +0400
@@ -16,7 +16,6 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
 
 import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
 
 import java.util.ArrayList;
 import java.util.Date;
@@ -29,8 +28,9 @@
   @NotNull private String myUser;
   @NotNull private Date myTimestamp;
   private String myDescription;
-  private boolean myContainsFiles;
-  private List<ChangeSetRevision> myParents;
+  private List<ChangeSetRevision> myParents = new ArrayList<ChangeSetRevision>();
+  private List<ModifiedFile> myModifiedFiles = new ArrayList<ModifiedFile>();
+
 
   public ChangeSet(final int revNumber, @NotNull final String id) {
     super(revNumber, id);
@@ -56,14 +56,7 @@
     myDescription = description;
   }
 
-  public void setContainsFiles(final boolean containsFiles) {
-    myContainsFiles = containsFiles;
-  }
-
   public void addParent(@NotNull ChangeSetRevision rev) {
-    if (myParents == null) {
-      myParents = new ArrayList<ChangeSetRevision>();
-    }
     myParents.add(rev);
   }
 
@@ -94,28 +87,29 @@
   }
 
   /**
-   * Returns parrents of this change set, or null if there were no parents.
+   * Returns parrents of this change set
    * @return see above
    */
-  @Nullable
+  @NotNull
   public List<ChangeSetRevision> getParents() {
     return myParents;
   }
 
   /**
-   * Returns true if this change has changed files
-   * @return see above
-   */
-  public boolean containsFiles() {
-    return myContainsFiles;
-  }
-
-
-  /**
    * Check if changeset is initial changeset (has no parents)
    * @return true if changeset is initial changeset
    */
   public boolean isInitial() {
-    return getParents() == null;
+    return getParents().isEmpty();
   }
+
+  public void setModifiedFiles(@NotNull final List<ModifiedFile> files) {
+    myModifiedFiles = files;
+  }
+
+  @NotNull
+  public List<ModifiedFile> getModifiedFiles() {
+    return myModifiedFiles;
+  }
+
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandUtil.java	Wed Sep 07 11:58:54 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandUtil.java	Wed Sep 07 12:40:50 2011 +0400
@@ -104,7 +104,7 @@
     removePrivateData(privateData, res);
 
     CommandUtil.checkCommandFailed(cmdStr, res);
-    Loggers.VCS.debug(res.getStdout());
+    Loggers.VCS.debug("Command " + cmdStr + " output:\n" + res.getStdout());
     return res;
   }
 
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Wed Sep 07 11:58:54 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Wed Sep 07 12:40:50 2011 +0400
@@ -16,12 +16,16 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
 
 import com.intellij.execution.configurations.GeneralCommandLine;
-import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.util.JDOMUtil;
 import jetbrains.buildServer.ExecResult;
 import jetbrains.buildServer.vcs.VcsException;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.JDOMException;
 import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
+import java.io.IOException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -30,22 +34,18 @@
 import java.util.Locale;
 
 public class LogCommand extends BaseCommand {
-  private final static Logger LOG = Logger.getInstance(LogCommand.class.getName());
+
+  private static final String DATE_FORMAT = "EEE MMM d HH:mm:ss yyyy Z";
+
   private String myFromId;
   private String myToId;
-  private ArrayList<String> myPaths;
   private Integer myLimit = null;
-  private static final String CHANGESET_PREFIX = "changeset:";
-  private static final String USER_PREFIX = "user:";
-  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 DESCRIPTION_PREFIX = "description:";
-  private static final String FILES_PREFIX = "files:";
   private String myBranchName;
+  private final File myTemplate;
 
-  public LogCommand(@NotNull Settings settings, @NotNull File workingDir) {
+  public LogCommand(@NotNull Settings settings, @NotNull File workingDir, @NotNull final File template) {
     super(settings, workingDir);
+    myTemplate = template;
     myBranchName = settings.getBranchName();
   }
 
@@ -57,10 +57,6 @@
     myToId = id;
   }
 
-  public void setPaths(final List<String> relPaths) {
-    myPaths = new ArrayList<String>(relPaths);
-  }
-
   public void setLimit(final int limit) {
     myLimit = limit;
   }
@@ -72,9 +68,7 @@
   public List<ChangeSet> execute() throws VcsException {
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("log");
-    cli.addParameter("-v");
-    cli.addParameter("--style");
-    cli.addParameter("default");
+    cli.addParameter("--style=" + myTemplate.getAbsolutePath());
     if (myBranchName != null) {
       cli.addParameter("-b");
       cli.addParameter(getSettings().getBranchName());
@@ -89,91 +83,115 @@
       cli.addParameter("--limit");
       cli.addParameter(myLimit.toString());
     }
-    if (myPaths != null) {
-      for (String path: myPaths) {
-        cli.addParameter(path);
-      }
+    ExecResult res = runCommand(cli);
+    try {
+      return parseChangeSetsXml(res.getStdout());
+    } catch (Exception e) {
+      throw new VcsException("Error while parsing log output:\n" + res.getStdout(), e);
     }
-
-    ExecResult res = runCommand(cli);
-    return parseChangeSets(res.getStdout());
   }
 
-  public static List<ChangeSet> parseChangeSets(final String stdout) {
-    List<ChangeSet> result = new ArrayList<ChangeSet>();
-    String[] lines = stdout.split("\n");
-    ChangeSet current = null;
-    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);
-          result.add(current);
-        } catch (IllegalArgumentException e) {
-          LOG.warn("Unable to extract changeset id from the line: " + line);
-        }
-
-        continue;
-      }
-
-      if (current == null) continue;
-
-      if (line.startsWith(USER_PREFIX)) {
-        current.setUser(line.substring(USER_PREFIX.length()).trim());
-        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));
-        continue;
-      }
+  private List<ChangeSet> parseChangeSetsXml(@NotNull final String xml) throws JDOMException, IOException, ParseException {
+    Document doc = JDOMUtil.loadDocument(xml);
+    Element log = doc.getRootElement();
+    return parseLog(log);
+  }
 
-      if (line.startsWith(DATE_PREFIX)) {
-        String date = line.substring(DATE_PREFIX.length()).trim();
-        try {
-          Date parsedDate = new SimpleDateFormat(DATE_FORMAT, Locale.ENGLISH).parse(date);
-          current.setTimestamp(parsedDate);
-        } catch (ParseException e) {
-          LOG.warn("Unable to parse date: " + date);
-          current = null;
-        }
-
-        continue;
-      }
 
-      if (line.startsWith(DESCRIPTION_PREFIX)) {
-        insideDescription = true;
-        continue;
-      }
-
-      if (insideDescription) {
-        descr.append(line).append("\n");
-      }
+  private List<ChangeSet> parseLog(@NotNull final Element logElement) throws ParseException {
+    List<ChangeSet> result = new ArrayList<ChangeSet>();
+    for (Object o : logElement.getChildren("logentry")) {
+      Element entry = (Element) o;
+      result.add(parseLogEntry(entry));
     }
-
-    if (insideDescription) {
-      current.setDescription(descr.toString().trim());
-    }
-
     return result;
   }
 
+
+
+  private ChangeSet parseLogEntry(@NotNull final Element logEntry) throws ParseException {
+    ChangeSet cset = new ChangeSet(getRevision(logEntry), getId(logEntry));
+    addParents(cset, logEntry);
+    cset.setUser(getAuthor(logEntry));
+    cset.setDescription(getDescription(logEntry));
+    cset.setTimestamp(getDate(logEntry));
+    cset.setModifiedFiles(getModifiedFiles(logEntry));
+    return cset;
+  }
+
+
+  private int getRevision(@NotNull final Element logEntry) {
+    return Integer.parseInt(logEntry.getAttribute("revision").getValue());
+  }
+
+
+  private String getId(@NotNull final Element logEntry) {
+    return logEntry.getAttribute("shortnode").getValue();
+  }
+
+
+  private void addParents(@NotNull final ChangeSet cset, @NotNull final Element logEntry) {
+    List parents = logEntry.getChildren("parent");
+    for (Object p : parents) {
+      Element parent = (Element) p;
+      ChangeSetRevision parentCset = getParent(parent);
+      cset.addParent(parentCset);
+    }
+  }
+
+
+  private ChangeSetRevision getParent(@NotNull final Element parent) {
+    return new ChangeSetRevision(getRevision(parent), getId(parent));
+  }
+
+
+  private String getAuthor(@NotNull final Element logEntry) {
+    Element author = logEntry.getChild("author");
+    return author.getAttribute("original").getValue();
+  }
+
+
+  private String getDescription(@NotNull final Element logEntry) {
+    Element msg = logEntry.getChild("msg");
+    return msg.getText();
+  }
+
+
+  private Date getDate(@NotNull final Element logEntry) throws ParseException {
+    Element date = logEntry.getChild("date");
+    return new SimpleDateFormat(DATE_FORMAT, Locale.ENGLISH).parse(date.getText());
+  }
+
+
+  private List<ModifiedFile> getModifiedFiles(@NotNull final Element logEntry) {
+    List<ModifiedFile> result = new ArrayList<ModifiedFile>();
+    Element paths = logEntry.getChild("paths");
+    for (Object o : paths.getChildren("path")) {
+      Element path = (Element) o;
+      result.add(getModifiedFile(path));
+    }
+    return result;
+  }
+
+
+  private ModifiedFile getModifiedFile(@NotNull final Element path) {
+    String filePath = path.getText();
+    ModifiedFile.Status status = getStatus(path);
+    return new ModifiedFile(status, filePath);
+  }
+
+
+  private ModifiedFile.Status getStatus(@NotNull final Element path) {
+    String action = path.getAttribute("action").getValue();
+    if (action.equals("A")) {
+      return ModifiedFile.Status.ADDED;
+    } else if (action.equals("M")) {
+      return ModifiedFile.Status.MODIFIED;
+    } else if (action.equals("R")) {
+      return ModifiedFile.Status.REMOVED;
+    } else {
+      return ModifiedFile.Status.UNKNOWN;
+    }
+  }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/resources/buildServerResources/log.template	Wed Sep 07 12:40:50 2011 +0400
@@ -0,0 +1,17 @@
+header = '<?xml version="1.0"?>\n<log>\n'
+footer = '</log>\n'
+
+changeset = '<logentry revision="{rev}" node="{node}" shortnode="{node|short}">\n{branches}{tags}{parents}<author original="{author|xmlescape}" email="{author|email|xmlescape}">{author|person|xmlescape}</author>\n<date>{date|date|xmlescape}</date>\n<msg xml:space="preserve">{desc|xmlescape}</msg>\n<paths>\n{file_adds}{file_dels}{file_mods}</paths>\n{file_copies}</logentry>\n'
+
+file_add  = '<path action="A">{file_add|xmlescape}</path>\n'
+file_mod  = '<path action="M">{file_mod|xmlescape}</path>\n'
+file_del  = '<path action="R">{file_del|xmlescape}</path>\n'
+
+start_file_copies = '<copies>\n'
+file_copy = '<copy source="{source|xmlescape}">{name|xmlescape}</copy>\n'
+end_file_copies = '</copies>\n'
+
+parent = '<parent revision="{rev}" node="{node}" shortnode="{node|short}"/>\n'
+branch = '<branch>{branch|xmlescape}</branch>\n'
+tag = '<tag>{tag|xmlescape}</tag>\n'
+extra = '<extra key="{key|xmlescape}">{value|xmlescape}</extra>\n'
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Wed Sep 07 11:58:54 2011 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Wed Sep 07 12:40:50 2011 +0400
@@ -54,17 +54,21 @@
  * <p>Personal builds (remote runs) are not yet supported, they require corresponding functionality from the IDE.
  */
 public class MercurialVcsSupport extends ServerVcsSupport implements LabelingSupport, VcsFileContentProvider, BranchSupport {
+
+  private final String LOG_TEMPLATE_NAME = "log.template";
   private ConcurrentMap<String, Lock> myWorkDirLocks= new ConcurrentHashMap<String, Lock>();
   private VcsManager myVcsManager;
   private File myDefaultWorkFolderParent;
   private MirrorManager myMirrorManager;
   private final PluginConfig myConfig;
+  private File myLogTemplate;
 
   public MercurialVcsSupport(@NotNull final VcsManager vcsManager,
                              @NotNull final ServerPaths paths,
                              @NotNull final SBuildServer server,
                              @NotNull final EventDispatcher<BuildServerListener> dispatcher,
-                             @NotNull final PluginConfig config) {
+                             @NotNull final PluginConfig config) throws IOException {
+    myLogTemplate = createLogTemplate(paths.getPluginDataDirectory());
     myVcsManager = vcsManager;
     myDefaultWorkFolderParent = new File(paths.getCachesDir(), "mercurial");
     myMirrorManager = new MirrorManager(myDefaultWorkFolderParent);
@@ -110,6 +114,14 @@
     return cfc.execute();
   }
 
+  private File createLogTemplate(@NotNull final File templateFileDir) throws IOException {
+    File template = new File(templateFileDir, LOG_TEMPLATE_NAME);
+    if (!template.exists()) {
+      FileUtil.copyResource(MercurialVcsSupport.class, "/buildServerResources/log.template", template);
+    }
+    return template;
+  }
+
   private List<VcsChange> toVcsChanges(final List<ModifiedFile> modifiedFiles, String prevVer, String curVer, CheckoutRules rules) {
     List<VcsChange> files = new ArrayList<VcsChange>();
     for (ModifiedFile mf: modifiedFiles) {
@@ -499,7 +511,7 @@
     String branchVersion = getCurrentVersion(branchRoot);
     String branchPoint = getBranchPoint(settings, baseVersion, branchVersion);
 
-    LogCommand lc = new LogCommand(settings, getWorkingDir(settings));
+    LogCommand lc = new LogCommand(settings, getWorkingDir(settings), myLogTemplate);
     lc.setFromRevId(new ChangeSetRevision(branchPoint).getId());
     lc.setToRevId(new ChangeSetRevision(branchVersion).getId());
     lc.setBranchName(null);//do not limit output to particular branch, return all commits
@@ -536,7 +548,7 @@
     if (branchOneRev.equals(branchTwoRev))
       return branchOneRev;
     File workingDir = getWorkingDir(settings);
-    LogCommand lc = new LogCommand(settings, workingDir);
+    LogCommand lc = new LogCommand(settings, workingDir, myLogTemplate);
     lc.setFromRevId(new ChangeSetRevision(branchOneRev).getId());
     lc.setToRevId(new ChangeSetRevision(branchTwoRev).getId());
     lc.setLimit(1);
@@ -559,10 +571,11 @@
 
         // first obtain changes between specified versions
         List<ModificationData> result = new ArrayList<ModificationData>();
-        if (currentVersion == null) return result;
+        if (currentVersion == null)
+          return result;
 
         File workingDir = getWorkingDir(settings);
-        LogCommand lc = new LogCommand(settings, workingDir);
+        LogCommand lc = new LogCommand(settings, workingDir, myLogTemplate);
         String fromId = new ChangeSetRevision(fromVersion).getId();
         lc.setFromRevId(fromId);
         lc.setToRevId(new ChangeSetRevision(currentVersion).getId());
@@ -571,36 +584,19 @@
           return result;
         }
 
-        // invoke status command for each changeset and determine what files were modified in these changesets
-        StatusCommand st = new StatusCommand(settings, workingDir);
         ChangeSet prev = new ChangeSet(fromVersion);
         for (ChangeSet cur : changeSets) {
-          if (cur.getId().equals(fromId)) continue; // skip already reported changeset
-
-          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();
-          }
+          if (cur.getId().equals(fromId))
+            continue; // skip already reported changeset
 
-          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)
+          boolean merge = cur.getParents().size() > 1;
+          List<ModifiedFile> modifiedFiles = cur.getModifiedFiles();
           List<VcsChange> files = toVcsChanges(modifiedFiles, prev.getFullVersion(), cur.getFullVersion(), checkoutRules);
-          if (files.isEmpty() && !merge) continue;
+          if (files.isEmpty() && !merge)
+            continue;
           ModificationData md = new ModificationData(cur.getTimestamp(), files, cur.getDescription(), cur.getUser(), root, cur.getFullVersion(), cur.getId());
-          if (merge) {
+          if (merge)
             md.setCanBeIgnored(false);
-          }
           result.add(md);
           prev = cur;
         }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Wed Sep 07 11:58:54 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Wed Sep 07 12:40:50 2011 +0400
@@ -457,11 +457,11 @@
 
 
   private void assertFiles(final List<String> expectedFiles, final ModificationData modificationData) {
-    List<String> actualFiles = new ArrayList<String>();
+    Set<String> actualFiles = new HashSet<String>();
     for (VcsChange vc: modificationData.getChanges()) {
       actualFiles.add(toFileStatus(vc.getType()) + " " + vc.getRelativeFileName());
     }
-    Assert.assertEquals("Actual files: " + actualFiles.toString(), expectedFiles, actualFiles);
+    Assert.assertEquals("Actual files: " + actualFiles.toString(), new HashSet<String>(expectedFiles), actualFiles);
   }
 
   private String toFileStatus(VcsChange.Type type) {
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java	Wed Sep 07 11:58:54 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java	Wed Sep 07 12:40:50 2011 +0400
@@ -15,8 +15,12 @@
  */
 package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
 
+import jetbrains.buildServer.TempFiles;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialVcsSupport;
+import jetbrains.buildServer.util.FileUtil;
 import jetbrains.buildServer.vcs.VcsException;
 import org.jetbrains.annotations.NotNull;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
@@ -26,13 +30,26 @@
 
 @Test
 public class LogCommandTest extends BaseCommandTestCase {
+
+  private TempFiles myTempFiles = new TempFiles();
+  private File myTemplateFile;
+
+
   @BeforeMethod
   @Override
   protected void setUp() throws Exception {
     super.setUp();
     setRepository("mercurial-tests/testData/rep1", true);
+    myTemplateFile = myTempFiles.createTempFile();
+    FileUtil.copyResource(MercurialVcsSupport.class, "/buildServerResources/log.template", myTemplateFile);
   }
 
+  @AfterMethod
+  public void tearDown() {
+    myTempFiles.cleanup();
+  }
+
+
   public void testOneChangeSet() throws Exception {
     final String toId = "9875b412a788";
     List<ChangeSet> changes = runLog(null, toId);
@@ -42,7 +59,7 @@
     assertEquals(toId, changeSet.getId());
     assertEquals("pavel@localhost", changeSet.getUser());
     assertEquals("dir1 created", changeSet.getDescription());
-    assertNull(changeSet.getParents());
+    assertTrue(changeSet.getParents().isEmpty());
   }
 
   public void testMoreThanOneChangeSet() throws Exception {
@@ -84,10 +101,36 @@
             "bbb", changes.get(0).getDescription());
   }
 
+  public void log_result_should_contain_changed_files() throws Exception {
+    final String fromId = "7209b1f1d793";
+    final String toId = "b06a290a363b";
+    List<ChangeSet> csets = runLog(fromId, toId);
+    assertEquals(3, csets.size());
+
+    List<ModifiedFile> files = csets.get(0).getModifiedFiles();
+    assertEquals(1, files.size());
+    ModifiedFile file = files.get(0);
+    assertEquals(ModifiedFile.Status.ADDED, file.getStatus());
+    assertEquals("dir1/file4.txt", file.getPath());
+
+    files = csets.get(1).getModifiedFiles();
+    assertEquals(1, files.size());
+    file = files.get(0);
+    assertEquals(ModifiedFile.Status.REMOVED, file.getStatus());
+    assertEquals("dir1/file4.txt", file.getPath());
+
+    files = csets.get(2).getModifiedFiles();
+    assertEquals(1, files.size());
+    file = files.get(0);
+    assertEquals(ModifiedFile.Status.MODIFIED, file.getStatus());
+    assertEquals("dir1/file3.txt", file.getPath());
+  }
+
+
   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, @NotNull File workingDir) throws VcsException {
-        LogCommand lc = new LogCommand(settings, workingDir);
+        LogCommand lc = new LogCommand(settings, workingDir, myTemplateFile);
         lc.setFromRevId(fromId);
         lc.setToRevId(toId);
         return lc.execute();
--- a/mercurial.ipr	Wed Sep 07 11:58:54 2011 +0400
+++ b/mercurial.ipr	Wed Sep 07 12:40:50 2011 +0400
@@ -28,6 +28,9 @@
     <option name="USE_USER_FILTER" value="false" />
     <option name="USER" value="" />
   </component>
+  <component name="ClearCaseSharedConfig">
+    <option name="myUseUcmModel" value="true" />
+  </component>
   <component name="CodeStyleProjectProfileManger">
     <option name="PROJECT_PROFILE" />
     <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" />
@@ -50,6 +53,7 @@
       <entry name="?*.tld" />
       <entry name="?*.jsp" />
       <entry name="?*.tag" />
+      <entry name="?*.template" />
     </wildcardResourcePatterns>
     <annotationProcessing enabled="false" useClasspath="true" />
   </component>
@@ -108,12 +112,13 @@
     <option name="PROJECT_PROFILE" value="Project Default" />
     <option name="USE_PROJECT_PROFILE" value="true" />
     <version value="1.0" />
-    <list size="5">
+    <list size="6">
       <item index="0" class="java.lang.String" itemvalue="TYPO" />
-      <item index="1" class="java.lang.String" itemvalue="INFO" />
-      <item index="2" class="java.lang.String" itemvalue="WARNING" />
-      <item index="3" class="java.lang.String" itemvalue="ERROR" />
-      <item index="4" class="java.lang.String" itemvalue="SERVER PROBLEM" />
+      <item index="1" class="java.lang.String" itemvalue="WEAK WARNING" />
+      <item index="2" class="java.lang.String" itemvalue="INFO" />
+      <item index="3" class="java.lang.String" itemvalue="WARNING" />
+      <item index="4" class="java.lang.String" itemvalue="ERROR" />
+      <item index="5" class="java.lang.String" itemvalue="SERVER PROBLEM" />
     </list>
   </component>
   <component name="JavadocGenerationManager">
@@ -386,6 +391,13 @@
       <JAVADOC />
       <SOURCES />
     </library>
+    <library name="jdom">
+      <CLASSES>
+        <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/jdom.jar!/" />
+      </CLASSES>
+      <JAVADOC />
+      <SOURCES />
+    </library>
     <library name="JMock">
       <CLASSES>
         <root url="jar://$PROJECT_DIR$/mercurial-tests/lib/hamcrest-library-1.1.jar!/" />