changeset 399:45f25ca68312 Faradi-7.0.x

Support local mirrors for subrepos
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Fri, 02 Mar 2012 14:24:58 +0400
parents 36b1bc5add89
children 68cc614b191b
files mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgDetector.java mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVersion.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubRepo.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/AuthSettings.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BranchesCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangedFilesCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Init.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeBaseCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ParseHgVersionException.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/TagCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnknownFileException.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnknownRevisionException.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnrelatedRepositoryException.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/ConnectionRefusedException.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/ParseHgVersionException.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/UnknownFileException.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/UnknownRevisionException.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/UnrelatedRepositoryException.java mercurial-server/src/META-INF/build-server-plugin-mercurial.xml 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/CommandFactoryImpl.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseNoRevsets.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseWithRevsets.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/RepoFactory.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgRepo.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-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeBaseCommand.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeBaseNoRevsets.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeBaseWithRevsets.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutWithSubreposTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVersionTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/UnrelatedResitoriesTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommandTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommandTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommandTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommandTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommandTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommandTest.java mercurial-tests/testData/subrepos/README mercurial-tests/testData/subrepos/http_r1/hg/00changelog.i mercurial-tests/testData/subrepos/http_r1/hg/branchheads.cache mercurial-tests/testData/subrepos/http_r1/hg/dirstate mercurial-tests/testData/subrepos/http_r1/hg/last-message.txt mercurial-tests/testData/subrepos/http_r1/hg/requires mercurial-tests/testData/subrepos/http_r1/hg/store/00changelog.i mercurial-tests/testData/subrepos/http_r1/hg/store/00manifest.i mercurial-tests/testData/subrepos/http_r1/hg/store/data/a.i mercurial-tests/testData/subrepos/http_r1/hg/store/data/b.i mercurial-tests/testData/subrepos/http_r1/hg/store/data/c.i mercurial-tests/testData/subrepos/http_r1/hg/store/fncache mercurial-tests/testData/subrepos/http_r1/hg/store/undo mercurial-tests/testData/subrepos/http_r1/hg/tags.cache mercurial-tests/testData/subrepos/http_r1/hg/undo.branch mercurial-tests/testData/subrepos/http_r1/hg/undo.desc mercurial-tests/testData/subrepos/http_r1/hg/undo.dirstate mercurial-tests/testData/subrepos/http_r2/hg/00changelog.i mercurial-tests/testData/subrepos/http_r2/hg/branchheads.cache mercurial-tests/testData/subrepos/http_r2/hg/dirstate mercurial-tests/testData/subrepos/http_r2/hg/last-message.txt mercurial-tests/testData/subrepos/http_r2/hg/requires mercurial-tests/testData/subrepos/http_r2/hg/store/00changelog.i mercurial-tests/testData/subrepos/http_r2/hg/store/00manifest.i mercurial-tests/testData/subrepos/http_r2/hg/store/data/a.i mercurial-tests/testData/subrepos/http_r2/hg/store/data/~2ehgsub.i mercurial-tests/testData/subrepos/http_r2/hg/store/data/~2ehgsubstate.i mercurial-tests/testData/subrepos/http_r2/hg/store/fncache mercurial-tests/testData/subrepos/http_r2/hg/store/undo mercurial-tests/testData/subrepos/http_r2/hg/tags.cache mercurial-tests/testData/subrepos/http_r2/hg/undo.branch mercurial-tests/testData/subrepos/http_r2/hg/undo.desc mercurial-tests/testData/subrepos/http_r2/hg/undo.dirstate mercurial-tests/testData/subrepos/http_r3/hg/00changelog.i mercurial-tests/testData/subrepos/http_r3/hg/branchheads.cache mercurial-tests/testData/subrepos/http_r3/hg/dirstate mercurial-tests/testData/subrepos/http_r3/hg/last-message.txt mercurial-tests/testData/subrepos/http_r3/hg/requires mercurial-tests/testData/subrepos/http_r3/hg/store/00changelog.i mercurial-tests/testData/subrepos/http_r3/hg/store/00manifest.i mercurial-tests/testData/subrepos/http_r3/hg/store/data/a.i mercurial-tests/testData/subrepos/http_r3/hg/store/data/~2ehgsub.i mercurial-tests/testData/subrepos/http_r3/hg/store/data/~2ehgsubstate.i mercurial-tests/testData/subrepos/http_r3/hg/store/fncache mercurial-tests/testData/subrepos/http_r3/hg/store/undo mercurial-tests/testData/subrepos/http_r3/hg/undo.branch mercurial-tests/testData/subrepos/http_r3/hg/undo.desc mercurial-tests/testData/subrepos/http_r3/hg/undo.dirstate
diffstat 114 files changed, 1529 insertions(+), 1178 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgDetector.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgDetector.java	Fri Mar 02 14:24:58 2012 +0400
@@ -68,7 +68,7 @@
   private boolean canRunHg(@NotNull final String hgPath, @NotNull final File workDir, boolean logWarnings) {
     VersionCommand versionCommand = new VersionCommand(hgPath, workDir);
     try {
-      HgVersion version = versionCommand.execute();
+      HgVersion version = versionCommand.call();
       if (isCompatible(version)) {
         return true;
       } else {
--- a/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java	Fri Mar 02 14:24:58 2012 +0400
@@ -1,11 +1,10 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial;
 
-import com.intellij.openapi.util.Pair;
 import jetbrains.buildServer.agent.AgentRunningBuild;
 import jetbrains.buildServer.agent.BuildProgressLogger;
 import jetbrains.buildServer.agent.vcs.IncludeRuleUpdater;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
-import jetbrains.buildServer.util.FileUtil;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.AuthSettings;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
 import jetbrains.buildServer.vcs.IncludeRule;
 import jetbrains.buildServer.vcs.VcsException;
 import jetbrains.buildServer.vcs.VcsRoot;
@@ -13,14 +12,9 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.Map;
 
 import static com.intellij.openapi.util.io.FileUtil.delete;
-import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandUtil.removePrivateData;
-import static jetbrains.buildServer.util.FileUtil.isEmptyDir;
 
 /**
  * @author dmitry.neverov
@@ -29,9 +23,9 @@
 
   private final AgentPluginConfig myConfig;
   private final MirrorManager myMirrorManager;
-  private final HgPathProvider myHgPathProvider;
-  private final VcsRoot myRoot;
   private final Settings mySettings;
+  private final AuthSettings myAuthSettings;
+  private final String myHgPath;
   private final String myToVersion;
   private final BuildProgressLogger myLogger;
   private final boolean myUseLocalMirrors;
@@ -45,9 +39,9 @@
                                      @NotNull final AgentRunningBuild build) {
     myConfig = pluginConfig;
     myMirrorManager = mirrorManager;
-    myHgPathProvider = hgPathProvider;
-    myRoot = root;
-    mySettings = new Settings(myHgPathProvider, myRoot);
+    mySettings = new Settings(hgPathProvider, root);
+    myAuthSettings = mySettings.getAuthSettings();
+    myHgPath = mySettings.getHgCommandPath();
     myToVersion = toVersion;
     myLogger = build.getBuildLogger();
     myUseLocalMirrors = myConfig.isUseLocalMirrors(build);
@@ -59,9 +53,9 @@
     try {
       checkRuleIsValid(rule);
       if (myUseLocalMirrors)
-        updateLocalMirror();
+        updateLocalMirror(mySettings.getRepository(), myToVersion);
       updateRepository(workingDir);
-      updateWorkingDir(workingDir);
+      updateWorkingDir(workingDir, myToVersion, mySettings.getRepository());
     } catch (Exception e) {
       throwVcsException(e);
     }
@@ -72,234 +66,122 @@
   }
 
 
-  private void throwVcsException(Exception e) throws VcsException {
-    if (e instanceof VcsException)
-      throw (VcsException) e;
-    else
-      throw new VcsException(e);
-  }
-
-
-  private void initRepository(Settings settings, File workingDir, boolean useLocalMirrors) throws VcsException {
-    try {
-      String defaultPullUrl = getDefaultPullUrl(settings, useLocalMirrors);
-      myLogger.message("Init repository at " + workingDir.getAbsolutePath() + ", remote repository is " +
-              removePrivateData(defaultPullUrl, Collections.singleton(settings.getPassword())));
-      new Init(settings, workingDir, defaultPullUrl).execute();
-    } catch (IOException e) {
-      throw new VcsException("Error while initializing repository at " + workingDir.getAbsolutePath(), e);
-    }
-  }
-
-
-  private void updateRepository(File workingDir) throws VcsException, IOException {
-    String defaultPullUrl = getDefaultPullUrl(mySettings, myUseLocalMirrors);
-    if (isEmptyDir(workingDir)) {
-      workingDir.mkdirs();
-      myLogger.message("Start cloning from " + removePrivateData(defaultPullUrl, Collections.singleton(mySettings.getPassword())));
-      CloneCommand clone = new CloneCommand(mySettings, workingDir);
-      clone.setRepository(defaultPullUrl);
-      clone.setUsePullProtocol(false);
-      clone.setUpdateWorkingDir(false);
-      clone.execute();
-      myLogger.message("Repository successfully cloned");
+  private void updateLocalMirror(@NotNull String repositoryUrl, @NotNull String revision) throws VcsException, IOException {
+    File mirrorDir = myMirrorManager.getMirrorDir(repositoryUrl);
+    HgRepo mirrorRepo = new HgRepo(mirrorDir, myHgPath, myAuthSettings);
+    if (!mirrorRepo.isValidRepository()) {
+      delete(mirrorDir);
+      myLogger.message("Clone repository " + myAuthSettings.getRepositoryUrlWithHiddenPassword(repositoryUrl) + " into local mirror " + mirrorRepo.path());
+      mirrorRepo.doClone().fromRepository(repositoryUrl)
+              .setUpdateWorkingDir(false)
+              .setUsePullProtocol(false)
+              .useUncompressedTransfer(mySettings.isUncompressedTransfer())
+              .call();
+      myLogger.message("Clone successfully finished");
     } else {
-      if (!Settings.isValidRepository(workingDir)) {
-        initRepository(mySettings, workingDir, myUseLocalMirrors);
+      myLogger.message("Update local mirror of " + myAuthSettings.getRepositoryUrlWithHiddenPassword(repositoryUrl) + " at " + mirrorDir);
+      if (mirrorRepo.containsRevision(revision)) {
+        myLogger.message("Local mirror is already up-to-date");
       } else {
-        ensureUseRightRepository(workingDir);
-      }
-      myLogger.message("Start pulling changes from " + removePrivateData(defaultPullUrl, Collections.singleton(mySettings.getPassword())));
-      new PullCommand(mySettings, workingDir, defaultPullUrl).execute(myPullTimeout);
-      myLogger.message("Changes successfully pulled");
-    }
-  }
-
-
-  private void ensureUseRightRepository(File workingDir) throws VcsException {
-    boolean clonedFromWrongRepository = myUseLocalMirrors && !isClonedFromLocalMirror(workingDir)
-                                     || !myUseLocalMirrors && isClonedFromLocalMirror(workingDir);
-
-    if (clonedFromWrongRepository) {
-      String rightRepository = myUseLocalMirrors ? "local mirror" : "remote repository";
-      String wrongRepository = myUseLocalMirrors ? "remote repository" : "local mirror";
-      myLogger.message("Repository in working directory is cloned from " + wrongRepository + ", clone it from " + rightRepository);
-      FileUtil.delete(workingDir);
-      initRepository(mySettings, workingDir, myUseLocalMirrors);
-    }
-  }
-
-
-  private void updateLocalMirror() throws VcsException, IOException {
-    Settings settings = new Settings(myHgPathProvider, myRoot);
-    File mirrorDir = myMirrorManager.getMirrorDir(settings.getRepository());
-    myLogger.message("Update local mirror at " + mirrorDir);
-    if (!Settings.isValidRepository(mirrorDir)) {
-      initRepository(settings, mirrorDir, false);
-    }
-    final String defaultPullUrl = getDefaultPullUrl(settings, false);
-    myLogger.message("Start pulling changes from " + removePrivateData(defaultPullUrl, Collections.singleton(settings.getPassword())));
-    new PullCommand(settings, mirrorDir).execute(myPullTimeout);
-    myLogger.message("Local mirror changes successfully pulled");
-  }
-
-
-  private void updateWorkingDir(@NotNull final File workingDir) throws VcsException, IOException {
-    String workingDirRevision = getWorkingDirRevision(mySettings, workingDir);
-    if (isInitialClone(workingDirRevision)) {
-      doUpdateWorkingDir(workingDir);
-    } else {
-      Map<String, Pair<String, String>> currentSubrepos = getSubrepositories(workingDir, workingDirRevision);
-      if (currentSubrepos.isEmpty()) {
-        doUpdateWorkingDir(workingDir);
-      } else {
-        Map<String, Pair<String, String>> toVersionSubrepos = getSubrepositories(workingDir, myToVersion);
-        Map<String, Pair<String, String>> subrepositoriesWithChangedUrls = getSubrepositoriesWithChangedUrls(currentSubrepos, toVersionSubrepos);
-        if (subrepositoriesWithChangedUrls.isEmpty()) {
-          doUpdateWorkingDir(workingDir);
-        } else {
-          logSubrepositoriesUrlsChanged(workingDirRevision, myToVersion, subrepositoriesWithChangedUrls);
-          FileUtil.delete(workingDir);
-          updateRepository(workingDir);
-          doUpdateWorkingDir(workingDir);
-        }
+        myLogger.message("Start pulling changes from " + myAuthSettings.getRepositoryUrlWithHiddenPassword(repositoryUrl));
+        mirrorRepo.pull().fromRepository(repositoryUrl)
+                .withTimeout(myPullTimeout)
+                .call();
+        myLogger.message("Local mirror changes successfully pulled");
       }
     }
   }
 
 
-  private void logSubrepositoriesUrlsChanged(@NotNull final String fromVersion,
-                                             @NotNull final String toVersion,
-                                             @NotNull final Map<String, Pair<String, String>> subrepositoriesWithChangedUrls) {
-    StringBuilder sb = new StringBuilder();
-    sb.append("Subrepositories' URLs were changed between revisions ").append(fromVersion).append("..").append(toVersion).append(":\n");
-    for (Map.Entry<String, Pair<String, String>> entry : subrepositoriesWithChangedUrls.entrySet()) {
-      String path = entry.getKey();
-      String oldUrl = entry.getValue().first;
-      String newUrl = entry.getValue().second;
-      sb.append("path: \"").append(path).append("\", old URL: \"").append(oldUrl).append("\", new URL: \"").append(newUrl).append("\";\n");
-    }
-    sb.append("do clean checkout.");
-    myLogger.warning(sb.toString());
-  }
-
-
-  /*returns map: relative path -> (repository url, repository revision)*/
-  private Map<String, Pair<String, String>> getSubrepositories(@NotNull final File workingDir, @NotNull final String revision) throws VcsException, IOException {
-    CatCommand cc = new CatCommand(mySettings, workingDir);
-    cc.setRevId(revision);
-    File catDir = null;
-    try {
-      catDir = cc.execute(Arrays.asList(".hgsub", ".hgsubstate"), false);
-      File hgsub = new File(catDir, ".hgsub");
-      File hgsubstate = new File(catDir, ".hgsubstate");
-      return readSubrepositories(hgsub, hgsubstate);
-    } catch (VcsException e) {
-      return Collections.emptyMap();
-    } finally {
-      if (catDir != null)
-        delete(catDir);
-    }
-  }
-
-
-  private Map<String, Pair<String, String>> readSubrepositories(@NotNull final File hgsub, @NotNull final File hgsubstate) throws IOException {
-    if (hgsub.exists() && hgsubstate.exists()) {
-      Map<String, String> path2repo = readHgsub(hgsub);
-      Map<String, String> path2revision = readHgsubstate(hgsubstate);
-      Map<String, Pair<String, String>> result = new HashMap<String, Pair<String, String>>();
-      for (Map.Entry<String, String> entry : path2repo.entrySet()) {
-        String path = entry.getKey();
-        String repo = entry.getValue();
-        String revision = path2revision.get(path);
-        if (revision != null) {
-          result.put(path, Pair.create(repo, revision));
-        } else {
-          myLogger.warning("Cannot find revision for subrepository at path " + path + " skip it");
-        }
+  private void updateRepository(@NotNull File workingDir) throws VcsException, IOException {
+    String repositoryUrl = getDefaultPullUrl(mySettings, myUseLocalMirrors);
+    HgRepo repo = new HgRepo(workingDir, myHgPath, myAuthSettings);
+    myLogger.message("Update repository " + workingDir.getAbsolutePath());
+    if (repo.isEmpty()) {//can do clone only in empty dir
+      myLogger.message("Start cloning from " + (myUseLocalMirrors ? "local mirror " : "") + myAuthSettings.getRepositoryUrlWithHiddenPassword(repositoryUrl));
+      repo.doClone().fromRepository(repositoryUrl)
+              .setUsePullProtocol(false)
+              .setUpdateWorkingDir(false)
+              .useUncompressedTransfer(!myUseLocalMirrors && mySettings.isUncompressedTransfer())
+              .call();
+      repo.setDefaultPath(mySettings.getRepository());
+      myLogger.message("Repository successfully cloned");
+    } else {
+      if (!repo.isValidRepository())
+        repo.init().call();
+      repo.setDefaultPath(mySettings.getRepository());
+      if (repo.containsRevision(myToVersion)) {
+        myLogger.message("Repository already contains revision " + myToVersion);
+      } else {
+        myLogger.message("Start pulling changes from " + (myUseLocalMirrors ? "local mirror " : "") + myAuthSettings.getRepositoryUrlWithHiddenPassword(repositoryUrl));
+        repo.pull().fromRepository(repositoryUrl)
+                .withTimeout(myPullTimeout)
+                .call();
+        myLogger.message("Changes successfully pulled");
       }
-      return result;
-    } else {
-      return Collections.emptyMap();
     }
   }
 
 
-  /*returns map: relative path -> repository url */
-  private Map<String, String> readHgsub(@NotNull final File hgsub) throws IOException {
-    Map<String, String> result = new HashMap<String, String>();
-    for (String line : FileUtil.readFile(hgsub)) {
-      String[] parts = line.split(" = ");
-      if (parts.length == 2) {
-        result.put(parts[0], parts[1]);
-      } else {
-        myLogger.warning("Cannot parse the line '" + line + "' from .hgsub, skip it");
-      }
-    }
-    return result;
+  private void updateWorkingDir(@NotNull File workingDir, @NotNull String toVersion, @NotNull String repositoryUrl) throws VcsException, IOException {
+    HgRepo repo = new HgRepo(workingDir, myHgPath, myAuthSettings);
+    updateSubrepositories(repo, toVersion, repositoryUrl);
+    doUpdateWorkingDir(repo, toVersion);
   }
 
-
-  /*returns map: relative path -> revision */
-  private Map<String, String> readHgsubstate(@NotNull final File hgsubstate) throws IOException {
-    Map<String, String> result = new HashMap<String, String>();
-    for (String line : FileUtil.readFile(hgsubstate)) {
-      String[] parts = line.split(" ");
-      if (parts.length == 2) {
-        result.put(parts[1], parts[0]);
+  private void updateSubrepositories(@NotNull HgRepo repo, @NotNull String toVersion, @NotNull String parentRepositoryUrl) throws VcsException, IOException {
+    if (!repo.hasSubreposAtRevision(toVersion))
+      return;
+    myLogger.message("Process subrepos of " + parentRepositoryUrl);
+    String workingDirRevision = repo.getWorkingDirRevision();
+    Map<String, SubRepo> workingDirSubrepos = repo.getSubrepositories(workingDirRevision);
+    Map<String, SubRepo> subrepos = repo.getSubrepositories(toVersion);
+    for (Map.Entry<String, SubRepo> entry : subrepos.entrySet()) {
+      String path = entry.getKey();
+      myLogger.message("Process subrepo " + path);
+      SubRepo subrepo = entry.getValue();
+      SubRepo workingDirSubrepo = workingDirSubrepos.get(path);
+      if (workingDirSubrepo != null && subrepo.hasDifferentUrlThan(workingDirSubrepo)) {
+        myLogger.message("The url of subrepo was changed between revisions " + workingDirRevision + " and " + toVersion + " , delete the subrepo");
+        delete(subrepo.dir());
+      }
+      String subrepoUrl = subrepo.url();
+      if (subrepo.isRelative()) {
+        subrepoUrl = parentRepositoryUrl.endsWith("/") ? parentRepositoryUrl + subrepo.url() : parentRepositoryUrl + "/" + subrepo.url();
+      }
+      HgRepo subrepository = new HgRepo(subrepo.dir(), myHgPath, myAuthSettings);
+      if (myUseLocalMirrors && subrepo.vcsType() == SubRepo.VcsType.hg) {
+        if (!subrepository.isValidRepository() || !subrepository.containsRevision(subrepo.revision())) {
+          updateLocalMirror(subrepoUrl, subrepo.revision());
+          File mirrorDir = myMirrorManager.getMirrorDir(subrepoUrl);
+          if (subrepository.isValidRepository()) {
+            myLogger.message("Pull from local mirror");
+            subrepository.pull().fromRepository(mirrorDir)
+                    .withTimeout(myPullTimeout)
+                    .call();
+            myLogger.message("done");
+          } else {
+            myLogger.message("Clone subrepo from local mirror");
+            subrepository.doClone().fromRepository(mirrorDir)
+                    .setUpdateWorkingDir(false)
+                    .setUsePullProtocol(false)
+                    .call();
+            subrepository.setDefaultPath(subrepo.url());
+            myLogger.message("done");
+          }
+        }
       } else {
-        myLogger.warning("Cannot parse the line '" + line + "' from .hgsubstate, skip it");
+        myLogger.message("Local mirrors aren't used, subrepo will be updated during the parent repo update");
       }
+      updateSubrepositories(subrepository, subrepo.revision(), subrepoUrl);
     }
-    return result;
   }
 
 
-  /*returns map repository path -> (old url, new url)*/
-  private Map<String, Pair<String, String>> getSubrepositoriesWithChangedUrls(@NotNull final Map<String, Pair<String, String>> subrepos1, @NotNull final Map<String, Pair<String, String>> subrepos2) {
-    Map<String, Pair<String, String>> result = new HashMap<String, Pair<String, String>>();
-    for (Map.Entry<String, Pair<String, String>> entry : subrepos1.entrySet()) {
-      String path = entry.getKey();
-      String url1 = entry.getValue().first;
-      Pair<String, String> urlRevision = subrepos2.get(path);
-      if (urlRevision != null && !url1.equals(urlRevision.first))
-        result.put(path, Pair.create(url1, urlRevision.first));
-    }
-    return result;
-  }
-
-
-  private boolean isInitialClone(@NotNull final String workingDirRevision) {
-    return "000000000000".equals(workingDirRevision);
-  }
-
-
-  private String getWorkingDirRevision(@NotNull final Settings settings, @NotNull final File workingDir) throws VcsException {
-    IdentifyCommand id = new IdentifyCommand(settings, workingDir);
-    id.setInLocalRepository(true);
-    return id.execute();
-  }
-
-
-  private void doUpdateWorkingDir(@NotNull final File workingDir) throws VcsException {
-    myLogger.message("Updating folder " + workingDir.getAbsolutePath() + " to revision " + myToVersion);
-    UpdateCommand update = new UpdateCommand(mySettings, workingDir);
-    addUpdateAuthConfigParams(update);
-    update.setToId(new ChangeSet(myToVersion).getId());
-    update.execute();
-    myLogger.message("Folder successfully updated");
-  }
-
-
-  private void addUpdateAuthConfigParams(@NotNull UpdateCommand update) {
-    String username = mySettings.getUsername();
-    String password = mySettings.getPassword();
-    if (username == null || password == null)
-      return;
-    update.withConfig("auth.tc.prefix", "*")
-          .withConfig("auth.tc.username", username)
-          .withConfig("auth.tc.password", password)
-          .withConfig("auth.tc.schemes", "http https");
+  private void doUpdateWorkingDir(@NotNull HgRepo repo, @NotNull String revision) throws VcsException {
+    myLogger.message("Updating working dir " + repo.path() + " to revision " + revision);
+    repo.update().toRevision(revision).call();
+    myLogger.message("Working dir updated");
   }
 
 
@@ -308,28 +190,23 @@
       File mirrorDir = myMirrorManager.getMirrorDir(settings.getRepository());
       return mirrorDir.getCanonicalPath();
     } else {
-      return settings.getRepositoryUrlWithCredentials();
+      return settings.getRepository();
     }
   }
 
 
   private void checkRuleIsValid(IncludeRule includeRule) throws VcsException {
     if (includeRule.getTo() != null && includeRule.getTo().length() > 0) {
-      if (!".".equals(includeRule.getFrom()) && includeRule.getFrom().length() != 0) {
+      if (!".".equals(includeRule.getFrom()) && includeRule.getFrom().length() != 0)
         throw new VcsException("Invalid include rule: " + includeRule.toString() + ", Mercurial plugin supports mapping of the form: +:.=>dir only.");
-      }
     }
   }
 
 
-  public boolean isClonedFromLocalMirror(@NotNull final File workingDir) {
-    try {
-      File mirrorDir = myMirrorManager.getMirrorDir(mySettings.getRepository());
-      File hgrc = new File(workingDir, ".hg" + File.separator + "hgrc");
-      String config = FileUtil.readText(hgrc);
-      return config.contains("default = " + mirrorDir.getCanonicalPath());
-    } catch (Exception e) {
-      return false;
-    }
+  private void throwVcsException(Exception e) throws VcsException {
+    if (e instanceof VcsException)
+      throw (VcsException) e;
+    else
+      throw new VcsException(e);
   }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,216 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
+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.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.intellij.openapi.util.io.FileUtil.delete;
+import static java.util.Collections.emptyMap;
+import static jetbrains.buildServer.util.FileUtil.isEmptyDir;
+
+/**
+* @author dmitry.neverov
+*/
+public class HgRepo {
+
+  protected final File myWorkingDir;
+  protected final String myHgPath;
+  protected final AuthSettings myAuthSettings;
+  private final Map<String, Map<String, SubRepo>> mySubreposCache = new HashMap<String, Map<String, SubRepo>>();
+
+  public HgRepo(@NotNull File workingDir, @NotNull String hgPath, @NotNull AuthSettings authSettings) {
+    myWorkingDir = workingDir;
+    myHgPath = hgPath;
+    myAuthSettings = authSettings;
+  }
+
+  public PullCommand pull() {
+    return new PullCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public PushCommand push() {
+    return new PushCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public CloneCommand doClone() {
+    return new CloneCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public IdentifyCommand id() {
+    return new IdentifyCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public Init init() {
+    return new Init(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public LogCommand log() {
+    return new LogCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public UpdateCommand update() {
+    return new UpdateCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public BranchesCommand branches() {
+    return new BranchesCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public StatusCommand status() {
+    return new StatusCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public TagCommand tag() {
+    return new TagCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public CatCommand cat() {
+    return new CatCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public ArchiveCommand archive() {
+    return new ArchiveCommand(myHgPath, myWorkingDir, myAuthSettings);
+  }
+
+  public String path() {
+    return myWorkingDir.getAbsolutePath();
+  }
+
+  public File getWorkingDir() {
+    return myWorkingDir;
+  }
+
+  public boolean isEmpty() {
+    return isEmptyDir(myWorkingDir);
+  }
+
+  public String getWorkingDirRevision() throws VcsException {
+    return id().inLocalRepository().call();
+  }
+
+  public boolean containsRevision(@NotNull String revision) {
+    return containsRevision(new ChangeSet(revision));
+  }
+
+  public boolean containsRevision(@NotNull ChangeSet cset) {
+    try {
+      id().revision(cset).inLocalRepository().call();
+      return true;
+    } catch (VcsException e) {
+      return false;
+    }
+  }
+
+  public boolean isValidRepository() {
+    // need better way to check that repository copy is ok
+    return myWorkingDir.isDirectory() && new File(myWorkingDir, ".hg").isDirectory();
+  }
+
+  public void setDefaultPath(@NotNull String defaultPath) {
+    File hgrc = new File(new File(myWorkingDir, ".hg"), "hgrc");
+    String content = "[paths]\ndefault = " + defaultPath;
+    FileUtil.writeFile(hgrc, content);
+  }
+
+  public boolean hasSubreposAtRevision(@NotNull String revision) {
+    return !getSubrepositories(new ChangeSet(revision)).isEmpty();
+  }
+
+  public boolean hasSubreposAtRevision(@NotNull ChangeSet cset) {
+    return !getSubrepositories(cset).isEmpty();
+  }
+
+  public Map<String, SubRepo> getSubrepositories(@NotNull String revision) {
+    return getSubrepositories(new ChangeSet(revision));
+  }
+
+  public Map<String, SubRepo> getSubrepositories(@NotNull ChangeSet cset) {
+    String revId = cset.getId();
+    Map<String, SubRepo> subrepos = mySubreposCache.get(revId);
+    if (subrepos != null)
+      return subrepos;
+    CatCommand cc = cat();
+    cc.setRevId(revId);
+    File catDir = null;
+    try {
+      catDir = cc.execute(Arrays.asList(".hgsub", ".hgsubstate"), false);
+      File hgsub = new File(catDir, ".hgsub");
+      File hgsubstate = new File(catDir, ".hgsubstate");
+      subrepos = readSubrepositories(hgsub, hgsubstate);
+      mySubreposCache.put(revId, subrepos);
+      return subrepos;
+    } catch (VcsException e) {
+      return emptyMap();
+    } finally {
+      if (catDir != null)
+        delete(catDir);
+    }
+  }
+
+  @Override
+  public String toString() {
+    return myWorkingDir.getAbsolutePath();
+  }
+
+  private Map<String, SubRepo> readSubrepositories(@NotNull final File hgsub, @NotNull final File hgsubstate) {
+    if (hgsub.exists() && hgsubstate.exists()) {
+      try {
+        Map<String, String> path2repo = readHgsub(hgsub);
+        Map<String, String> path2revision = readHgsubstate(hgsubstate);
+        Map<String, SubRepo> result = new HashMap<String, SubRepo>();
+        for (Map.Entry<String, String> entry : path2repo.entrySet()) {
+          String path = entry.getKey();
+          String url = entry.getValue();
+          String revision = path2revision.get(path);
+          if (revision != null) {
+            result.put(path, new SubRepo(this, path, url, revision));
+          } else {
+//              myLogger.warning("Cannot find revision for subrepository at path " + path + " skip it");
+          }
+        }
+        return result;
+      } catch (IOException e) {
+//          myLogger.warning("Error while trying to read subrepositories " + e.getMessage());
+        return emptyMap();
+      }
+    } else {
+      return emptyMap();
+    }
+  }
+
+  /*returns map: relative path -> repository url */
+  private Map<String, String> readHgsub(@NotNull final File hgsub) throws IOException {
+    Map<String, String> result = new HashMap<String, String>();
+    for (String line : FileUtil.readFile(hgsub)) {
+      String[] parts = line.split(" = ");
+      if (parts.length == 2) {
+        result.put(parts[0], parts[1]);
+      } else {
+//          myLogger.warning("Cannot parse the line '" + line + "' from .hgsub, skip it");
+      }
+    }
+    return result;
+  }
+
+
+  /*returns map: relative path -> revision */
+  private Map<String, String> readHgsubstate(@NotNull final File hgsubstate) throws IOException {
+    Map<String, String> result = new HashMap<String, String>();
+    for (String line : FileUtil.readFile(hgsubstate)) {
+      String[] parts = line.split(" ");
+      if (parts.length == 2) {
+        result.put(parts[1], parts[0]);
+      } else {
+//          myLogger.warning("Cannot parse the line '" + line + "' from .hgsubstate, skip it");
+      }
+    }
+    return result;
+  }
+}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVersion.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVersion.java	Fri Mar 02 14:24:58 2012 +0400
@@ -1,6 +1,6 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial;
 
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.ParseHgVersionException;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.ParseHgVersionException;
 import org.jetbrains.annotations.NotNull;
 
 /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SubRepo.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,74 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+/**
+* @author dmitry.neverov
+*/
+public class SubRepo {
+  private final HgRepo myParentRepo;
+  private final String myPath;
+  private final String myUrl;
+  private final String myRevision;
+  private final VcsType myVcsType;
+
+  public SubRepo(@NotNull HgRepo parentRepo, @NotNull String path, @NotNull String url, @NotNull String revision) {
+    myParentRepo = parentRepo;
+    myPath = path;
+    myUrl = url;
+    myRevision = revision;
+    myVcsType = parseVcsType();
+  }
+
+  @NotNull
+  public String path() {
+    return myPath;
+  }
+
+  @NotNull
+  public String url() {
+    return myUrl;
+  }
+
+  @NotNull
+  public String revision() {
+    return myRevision;
+  }
+
+  @NotNull
+  public VcsType vcsType() {
+    return myVcsType;
+  }
+
+  public boolean isRelative() {
+    return myUrl.startsWith("..");
+  }
+
+  @NotNull
+  File dir() {
+    return new File(myParentRepo.getWorkingDir(), myPath);
+  }
+
+  public boolean hasDifferentUrlThan(@NotNull SubRepo other) {
+    return !myUrl.equals(other.url());
+  }
+
+  @Override
+  public String toString() {
+    return myPath + " = " + myUrl + "#" + myRevision;
+  }
+
+  private VcsType parseVcsType() {
+    if (myUrl.startsWith("[svn]"))
+      return VcsType.svn;
+    if (myUrl.startsWith("[git]"))
+      return VcsType.git;
+    return VcsType.hg;
+  }
+
+  public static enum VcsType {
+    hg, git, svn
+  }
+}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -26,19 +26,21 @@
   private File myDestDir;
   private String myToId;
 
-  public ArchiveCommand(@NotNull final Settings settings, @NotNull final File workingDir) {
-    super(settings, workingDir);
+  public ArchiveCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
   }
 
-  public void setDestDir(@NotNull File destDir) {
+  public ArchiveCommand toDir(@NotNull File destDir) {
     myDestDir = destDir;
+    return this;
   }
 
-  public void setToId(final String toId) {
-    myToId = toId;
+  public ArchiveCommand revision(@NotNull ChangeSet cset) {
+    myToId = cset.getId();
+    return this;
   }
 
-  public void execute() throws VcsException {
+  public void call() throws VcsException {
     if (myDestDir == null)
       throw new IllegalStateException("Destination dir must be specified");
     GeneralCommandLine cli = createCommandLine();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/AuthSettings.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,127 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import jetbrains.buildServer.log.Loggers;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.net.*;
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.intellij.openapi.util.text.StringUtil.isEmpty;
+import static java.util.Arrays.asList;
+
+/**
+ * @author dmitry.neverov
+ */
+public class AuthSettings {
+
+  private final static Set<String> AUTH_PROTOS = new HashSet<String>(asList("http", "https", "ssh"));
+  private final String myUsername;
+  private final String myPassword;
+
+  public AuthSettings() {
+    this(null, null);
+  }
+
+  public AuthSettings(@Nullable String username, @Nullable String password) {
+    myUsername = username;
+    myPassword = password;
+  }
+
+  public String getUsername() {
+    return myUsername;
+  }
+
+  public String getPassword() {
+    return myPassword;
+  }
+
+  public String getRepositoryUrlWithCredentials(@NotNull String repositoryUrl) {
+    if (isRequireCredentials(repositoryUrl)) {
+      if (containsCredentials(repositoryUrl))
+        return repositoryUrl;
+      try {
+        return createURLWithCredentials(repositoryUrl);
+      } catch (MalformedURLException e) {
+        Loggers.VCS.warn("Error while parsing url " + repositoryUrl, e);
+      }
+      return repositoryUrl;
+    } else {
+      return repositoryUrl;
+    }
+  }
+
+  public String getRepositoryUrlWithHiddenPassword(@NotNull String repositoryUrl) {
+    if (isEmpty(myPassword))
+      return repositoryUrl;
+    return repositoryUrl.replace(myPassword, "******");
+  }
+
+  private boolean isRequireCredentials(@NotNull String repositoryUrl) {
+    for (String scheme : AUTH_PROTOS) {
+      if (repositoryUrl.startsWith(scheme + ":"))
+        return true;
+    }
+    return false;
+  }
+
+  private boolean containsCredentials(final String repositoryUrl) {
+    try {
+      URL url = new URL(null, repositoryUrl, new FakeStreamHandler());
+      String userInfo = url.getUserInfo();
+      return userInfo != null && userInfo.contains(":");
+    } catch (MalformedURLException e) {
+      return false;
+    }
+  }
+
+  private String createURLWithCredentials(String originalUrl) throws MalformedURLException {
+    String userInfo = createUserInfo();
+    if (!isEmpty(userInfo)) {
+      URL url = new URL(null, originalUrl, new FakeStreamHandler());
+      return url.getProtocol() + "://"
+              + userInfo + "@"
+              + url.getHost()
+              + (url.getPort() != -1 ? ":" + url.getPort() : "")
+              + url.getFile()
+              + (url.getRef() != null ? url.getRef() : "");
+    } else {
+      return originalUrl;
+    }
+  }
+
+  private String createUserInfo() {
+    String userInfo = "";
+    if (!isEmpty(myUsername)) {
+      userInfo += myUsername;
+      if (!isEmpty(myPassword)) {
+        userInfo += ":" + myPassword;
+      }
+    }
+    return getEscapedUserInfo(userInfo);
+  }
+
+  private static String getEscapedUserInfo(String userInfo) {
+    try {
+      URI uri = new URI("http", userInfo, "somewhere.com", 80, "", "", "");
+      String escapedURI = uri.toASCIIString();
+      int from = "http://".length();
+      int to = escapedURI.indexOf("somewhere.com") - 1;
+      String escapedUserInfo = escapedURI.substring(from, to);
+      escapedUserInfo = escapedUserInfo.replaceAll("&", "%26");
+      return escapedUserInfo;
+   } catch (URISyntaxException e) {
+      assert false;
+    }
+    return userInfo;
+  }
+
+  private class FakeStreamHandler extends URLStreamHandler {
+    @Override
+    protected URLConnection openConnection(URL u) throws IOException {
+      throw new UnsupportedOperationException();
+    }
+  }
+}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -57,4 +57,8 @@
   protected Set<String> getPrivateData() {
     return emptySet();
   }
+
+  protected String getHgPath() {
+    return myHgPath;
+  }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BranchesCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BranchesCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -31,8 +31,8 @@
  */
 public class BranchesCommand extends VcsRootCommand {
 
-  public BranchesCommand(@NotNull Settings settings, @NotNull File workingDir) {
-    super(settings, workingDir);
+  public BranchesCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
   }
 
   /**
@@ -40,7 +40,7 @@
    * @return see above
    * @throws jetbrains.buildServer.vcs.VcsException if error occurs
    */
-  public Map<String, ChangeSet> execute() throws VcsException {
+  public Map<String, ChangeSet> call() throws VcsException {
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("branches");
     CommandResult res = runCommand(cli);
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -22,23 +22,52 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Queue;
 
 import static com.intellij.openapi.util.io.FileUtil.delete;
+import static java.util.Collections.singletonList;
 import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with;
 
 public class CatCommand extends VcsRootCommand {
   private String myRevId;
   private final static int MAX_CMD_LEN = 900;
+  private List<String> myRelativePaths = new ArrayList<String>();
+  private boolean myCheckForFailure = true;
 
-  public CatCommand(@NotNull Settings settings, @NotNull File workingDir) {
-    super(settings, workingDir);
+  public CatCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
   }
 
-  public void setRevId(final String revId) {
+  public CatCommand files(@NotNull String relativePath) {
+    myRelativePaths = singletonList(relativePath);
+    return this;
+  }
+
+  public CatCommand files(@NotNull List<String> relativePaths) {
+    myRelativePaths = relativePaths;
+    return this;
+  }
+
+  public CatCommand setRevId(final String revId) {
     myRevId = revId;
+    return this;
+  }
+
+  public CatCommand atRevision(@NotNull ChangeSet cset) {
+    myRevId = cset.getId();
+    return this;
+  }
+
+  public CatCommand checkForFailure(boolean doCheckForFailure) {
+    myCheckForFailure = doCheckForFailure;
+    return this;
+  }
+
+  public File call() throws VcsException {
+    return execute(myRelativePaths, myCheckForFailure);
   }
 
   public File execute(List<String> relPaths) throws VcsException {
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangedFilesCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-/*
- * Copyright 2000-2011 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
-
-import com.intellij.execution.configurations.GeneralCommandLine;
-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 VcsRootCommand {
-  private String myRevId;
-
-  public ChangedFilesCommand(@NotNull Settings settings, @NotNull File workingDir) {
-    super(settings, workingDir);
-  }
-
-  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());
-
-      CommandResult 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/CloneCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -20,6 +20,7 @@
 import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
+import java.io.IOException;
 
 import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with;
 
@@ -29,34 +30,48 @@
   private String myRepository;
   private File myWorkingDir;
   private boolean myUsePullProtocol = true;
+  private boolean myUseUncompressedTransfer = false;
 
-  public CloneCommand(@NotNull Settings settings, @NotNull File workingDir) {
-    super(settings, workingDir);
+  public CloneCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
     myWorkingDir = workingDir;
-    myRepository = getSettings().getRepositoryUrlWithCredentials();
   }
 
-  /**
-   * Sets repository to clone, by default uses repository from the specified settings
-   * @param repo repository path
-   */
-  public void setRepository(@NotNull String repo) {
-    myRepository = repo;
+  public CloneCommand fromRepository(@NotNull String repositoryUrl) {
+    myRepository = repositoryUrl;
+    return this;
   }
 
-  public void setToId(final String toId) {
-    myToId = toId;
+  public CloneCommand fromRepository(@NotNull File localRepository) throws IOException {
+    return fromRepository(localRepository.getCanonicalPath());
   }
 
-  public void setUpdateWorkingDir(final boolean updateWorkingDir) {
-    myUpdateWorkingDir = updateWorkingDir;
+  public CloneCommand toRevision(@NotNull String revision) {
+    return toRevision(new ChangeSet(revision));
   }
 
-  public void setUsePullProtocol(boolean usePullProtocol) {
-    myUsePullProtocol = usePullProtocol;
+  public CloneCommand toRevision(@NotNull ChangeSet cset) {
+    myToId = cset.getId();
+    return this;
   }
 
-  public void execute() throws VcsException {
+  public CloneCommand setUpdateWorkingDir(boolean doUpdateWorkingDir) {
+    myUpdateWorkingDir = doUpdateWorkingDir;
+    return this;
+  }
+
+  public CloneCommand useUncompressedTransfer(boolean doUseUncompressedTransfer) {
+    myUseUncompressedTransfer = doUseUncompressedTransfer;
+    return this;
+  }
+
+  public CloneCommand setUsePullProtocol(boolean usePullProtocol) {
+    myUsePullProtocol = usePullProtocol;
+    return this;
+  }
+
+  public void call() throws VcsException {
+    myWorkingDir.mkdirs();
     GeneralCommandLine cli = createCommandLine();
     File parent = myWorkingDir.getParentFile();
     cli.setWorkDirectory(parent.getAbsolutePath());
@@ -70,10 +85,11 @@
     if (!myUpdateWorkingDir) {
       cli.addParameter("-U");
     }
-    if (getSettings().isUncompressedTransfer()) {
+    if (myUseUncompressedTransfer) {
       cli.addParameter("--uncompressed");
     }
-    cli.addParameter(myRepository);
+    String repositoryUrl = myAuthSettings.getRepositoryUrlWithCredentials(myRepository);
+    cli.addParameter(repositoryUrl);
     cli.addParameter(myWorkingDir.getName());
 
     runCommand(cli, with().timeout(24 * 3600)); // some repositories are quite large, we set timeout to 24 hours
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java	Fri Mar 02 14:24:58 2012 +0400
@@ -2,6 +2,10 @@
 
 import com.intellij.openapi.diagnostic.Logger;
 import jetbrains.buildServer.ExecResult;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.ConnectionRefusedException;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnknownFileException;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnknownRevisionException;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnrelatedRepositoryException;
 import jetbrains.buildServer.util.StringUtil;
 import jetbrains.buildServer.vcs.VcsException;
 import org.jetbrains.annotations.NotNull;
@@ -142,6 +146,7 @@
     checkUnrelatedRepository(stderr);
     checkUnknownRevision(stderr);
     checkFileNotUnderTheRoot(stderr);
+    checkConnectionRefused(stderr);
   }
 
   private void checkUnrelatedRepository(@NotNull final String stderr) throws UnrelatedRepositoryException {
@@ -173,6 +178,11 @@
     }
   }
 
+  private void checkConnectionRefused(@NotNull final String stderr) throws ConnectionRefusedException {
+    if (stderr.equals("abort: error: Connection refused"))
+      throw new ConnectionRefusedException();
+  }
+
   private static Set<Integer> setOf(Integer... ints) {
     return new HashSet<Integer>(asList(ints));
   }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -32,30 +32,50 @@
   private boolean myInLocalRepository = false;
   private ChangeSet myChangeSet;
   private Integer myRevisionNumber;
+  private AuthSettings myAuthSettings;
+  private String myRepositoryUrl;
 
-  public IdentifyCommand(@NotNull Settings settings, @NotNull File workingDir) {
-    super(settings, workingDir);
-  }
-
-  public void setInLocalRepository(boolean inLocalRepository) {
-    myInLocalRepository = inLocalRepository;
+  public IdentifyCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
   }
 
-  public void setChangeSet(ChangeSet changeSet) {
-    myChangeSet = changeSet;
+  public IdentifyCommand revision(@NotNull String revision) {
+    myChangeSet = new ChangeSet(revision);
+    return this;
   }
 
-  public void setRevisionNumber(int revisionNumber) {
-    myRevisionNumber = revisionNumber;
+  public IdentifyCommand revision(@NotNull ChangeSet cset) {
+    myChangeSet = cset;
+    return this;
   }
 
-  public String execute() throws VcsException {
+  public IdentifyCommand inLocalRepository() {
+    myInLocalRepository = true;
+    return this;
+  }
+
+  public IdentifyCommand revisionNumber(int revisionNumber) {
+    myRevisionNumber = revisionNumber;
+    return this;
+  }
+
+  public IdentifyCommand withAuthSettings(@NotNull AuthSettings authSettings) {
+    myAuthSettings = authSettings;
+    return this;
+  }
+
+  public IdentifyCommand repository(@NotNull String repositoryUrl) {
+    myRepositoryUrl = repositoryUrl;
+    return this;
+  }
+
+  public String call() throws VcsException {
     GeneralCommandLine cli = createCL();
     cli.addParameter("identify");
     if (myInLocalRepository) {
       cli.setWorkDirectory(this.getWorkDirectory().getAbsolutePath());
     } else {
-      cli.addParameter(getSettings().getRepositoryUrlWithCredentials());
+      cli.addParameter(myAuthSettings.getRepositoryUrlWithCredentials(myRepositoryUrl));
     }
     if (myChangeSet != null) {
       cli.addParameter("--rev");
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Init.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Init.java	Fri Mar 02 14:24:58 2012 +0400
@@ -1,7 +1,6 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
 
 import com.intellij.execution.configurations.GeneralCommandLine;
-import jetbrains.buildServer.util.FileUtil;
 import jetbrains.buildServer.vcs.VcsException;
 import org.jetbrains.annotations.NotNull;
 
@@ -12,25 +11,14 @@
  */
 public class Init extends VcsRootCommand {
 
-  private final String myDefaultPullUrl;
-
-  public Init(@NotNull final Settings settings, @NotNull File workingDir, @NotNull String defaultPullUrl) {
-    super(settings, workingDir);
-    myDefaultPullUrl = defaultPullUrl;
+  public Init(@NotNull final String hgPath, @NotNull final File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
   }
 
-  public void execute() throws VcsException {
+  public void call() throws VcsException {
     getWorkDirectory().mkdirs();
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("init");
     runCommand(cli);
-    writeDefaultPath();
-  }
-
-
-  private void writeDefaultPath() {
-    File hgrc = new File(new File(getWorkDirectory(), ".hg"), "hgrc");
-    String content = "[paths]\ndefault = " + myDefaultPullUrl;
-    FileUtil.writeFile(hgrc, content);
   }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -22,6 +22,7 @@
 import org.jdom.Element;
 import org.jdom.JDOMException;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
 import java.io.IOException;
@@ -41,48 +42,68 @@
   private String myBranchName;
   private boolean myCalculateParents = true;
   private String myRevsets;
-  private final File myTemplate;
+  private File myTemplate;
 
-  public LogCommand(@NotNull Settings settings, @NotNull File workingDir, @NotNull final File template) {
-    super(settings, workingDir);
-    myTemplate = template;
-    myBranchName = settings.getBranchName();
-  }
-
-  public void setFromRevId(String id) {
-    myFromId = id;
-  }
-
-  public void setToRevId(String id) {
-    myToId = id;
+  public LogCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
   }
 
-  public void setLimit(final int limit) {
-    myLimit = limit;
-  }
-
-  public void showCommitsFromAllBranches() {
-    myBranchName = null;
+  public LogCommand withTemplate(@NotNull File template) {
+    myTemplate = template;
+    return this;
   }
 
-  public void setCalculateParents(boolean doCalculate) {
-    myCalculateParents = doCalculate;
+  public LogCommand inBranch(@NotNull String branchName) {
+    myBranchName = branchName;
+    return this;
   }
 
-  public void setRevsets(String revsets) {
-    myRevsets = revsets;
+  public LogCommand fromRevision(@Nullable String fromRevision) {
+    myFromId = fromRevision == null ? fromRevision : new ChangeSet(fromRevision).getId();
+    return this;
   }
 
-  public List<ChangeSet> execute() throws VcsException {
+  public LogCommand toRevision(@Nullable String toRevision) {
+    myToId = toRevision == null ? toRevision: new ChangeSet(toRevision).getId();
+    return this;
+  }
+
+  public LogCommand toNamedRevision(@Nullable String namedRevision) {
+    myToId = namedRevision;
+    return this;
+  }
+
+  public LogCommand setLimit(final int limit) {
+    myLimit = limit;
+    return this;
+  }
+
+  public LogCommand showCommitsFromAllBranches() {
+    myBranchName = null;
+    return this;
+  }
+
+  public LogCommand dontCalculateParents() {
+    myCalculateParents = false;
+    return this;
+  }
+
+  public LogCommand withRevsets(String revsets) {
+    myRevsets = revsets;
+    return this;
+  }
+
+  public List<ChangeSet> call() throws VcsException {
     GeneralCommandLine cli = createCommandLine();
     cli.setCharset(Charset.forName("UTF-8"));
     cli.addParameters("--encoding", "UTF-8");
     cli.addParameter("log");
     cli.addParameter("-v");
-    cli.addParameter("--style=" + myTemplate.getAbsolutePath());
+    if (myTemplate != null)
+      cli.addParameter("--style=" + myTemplate.getAbsolutePath());
     if (myBranchName != null) {
       cli.addParameter("-b");
-      cli.addParameter(getSettings().getBranchName());
+      cli.addParameter(myBranchName);
     }
     cli.addParameter("-r");
     if (myRevsets != null) {
@@ -239,9 +260,9 @@
   private String getIdOf(int revNumber) throws VcsException {
     if (revNumber < 0)
       return ZERO_PARENT_ID;
-    IdentifyCommand identify = new IdentifyCommand(getSettings(), getWorkDirectory());
-    identify.setInLocalRepository(true);
-    identify.setRevisionNumber(revNumber);
-    return identify.execute();
+    return new IdentifyCommand(getHgPath(), getWorkDirectory(), myAuthSettings)
+            .revisionNumber(revNumber)
+            .inLocalRepository()
+            .call();
   }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeBaseCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
-
-import jetbrains.buildServer.vcs.VcsException;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * Analog of git merge-base. It returns a last common ancestor between two revisions.
- *
- * @author dmitry.neverov
- */
-public interface MergeBaseCommand {
-
-  /**
-   * Returns hash of least common ancestor between two revisions or null
-   * if common ancestor is not found
-   * @param revision1 first revision
-   * @param revision2 second revision
-   * @return see above
-   * @throws VcsException if some commands fail
-   */
-  @Nullable
-  String execute(@NotNull String revision1, @NotNull String revision2) throws VcsException;
-
-}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ParseHgVersionException.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
-
-import jetbrains.buildServer.vcs.VcsException;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * @author dmitry.neverov
- */
-public class ParseHgVersionException extends VcsException {
-
-  public ParseHgVersionException(@NotNull String version) {
-    super("Cannot parse version '" + version + "'");
-  }
-
-}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -20,6 +20,7 @@
 import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
+import java.io.IOException;
 
 import static com.intellij.openapi.util.io.FileUtil.delete;
 import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with;
@@ -30,23 +31,35 @@
  */
 public class PullCommand extends VcsRootCommand {
 
-  private final String myPullUrl;
+  private String myPullUrl;
+  private int myTimeout;
 
-  public PullCommand(@NotNull Settings settings, @NotNull File workingDir) {
-    this(settings, workingDir, settings.getRepositoryUrlWithCredentials());
+  public PullCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
   }
 
-  public PullCommand(@NotNull Settings settings, @NotNull File workingDir, @NotNull String pullUrl) {
-    super(settings, workingDir);
+  public PullCommand fromRepository(@NotNull String pullUrl) {
     myPullUrl = pullUrl;
+    return this;
   }
 
-  public void execute(int timeout) throws VcsException {
+  public PullCommand fromRepository(@NotNull File localRepository) throws IOException {
+    myPullUrl = localRepository.getCanonicalPath();
+    return this;
+  }
+
+  public PullCommand withTimeout(int timeout) {
+    myTimeout = timeout;
+    return this;
+  }
+
+  public void call() throws VcsException {
     ensureRepositoryIsNotLocked();
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("pull");
-    cli.addParameter(myPullUrl);
-    runCommand(cli, with().timeout(timeout));
+    String pullUrl = myAuthSettings != null ? myAuthSettings.getRepositoryUrlWithCredentials(myPullUrl) : myPullUrl;
+    cli.addParameter(pullUrl);
+    runCommand(cli, with().timeout(myTimeout));
   }
 
   private void ensureRepositoryIsNotLocked() {
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -27,23 +27,23 @@
  * @author pavel
  */
 public class PushCommand extends VcsRootCommand {
-  private boolean myForced;
 
-  public PushCommand(@NotNull Settings settings, @NotNull File workingDir) {
-    super(settings, workingDir);
+  private String myRepositoryUrl;
+
+  public PushCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
   }
 
-  public void setForce(boolean force) {
-    myForced = force;
+  public PushCommand toRepository(@NotNull String repositoryUrl) {
+    myRepositoryUrl = repositoryUrl;
+    return this;
   }
 
-  public void execute() throws VcsException {
+  public void call() throws VcsException {
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("push");
-    if (myForced) {
-      cli.addParameter("-f");
-    }
-    cli.addParameter(getSettings().getRepositoryUrlWithCredentials());
+    String repositoryUrl = myAuthSettings != null ? myAuthSettings.getRepositoryUrlWithCredentials(myRepositoryUrl) : myRepositoryUrl;
+    cli.addParameter(repositoryUrl);
     runCommand(cli, with().failureWhenStderrNotEmpty());
   }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java	Fri Mar 02 14:24:58 2012 +0400
@@ -18,17 +18,12 @@
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.Constants;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.HgPathProvider;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.PathUtil;
-import jetbrains.buildServer.log.Loggers;
 import jetbrains.buildServer.util.StringUtil;
 import jetbrains.buildServer.vcs.VcsRoot;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
-import java.io.IOException;
-import java.net.*;
-import java.util.HashSet;
-import java.util.Set;
 
 /**
  * Represents Mercurial repository settings
@@ -46,6 +41,7 @@
   private static final String DEFAULT_BRANCH_NAME = "default";
   private String myCustomClonePath;
   private final String myUserForTag;
+  private final AuthSettings myAuthSettings;
 
   public Settings(@NotNull final HgPathProvider hgPathProvider, @NotNull final VcsRoot vcsRoot) {
     myHgPathProvider = hgPathProvider;
@@ -57,6 +53,7 @@
     myPassword = vcsRoot.getProperty(Constants.PASSWORD);
     myUncompressedTransfer = "true".equals(vcsRoot.getProperty(Constants.UNCOMPRESSED_TRANSFER));
     myUserForTag = vcsRoot.getProperty(Constants.USER_FOR_TAG);
+    myAuthSettings = new AuthSettings(myUsername, myPassword);
   }
 
   public String getCustomClonePath() {
@@ -116,86 +113,8 @@
     return myUserForTag;
   }
 
-  private final static Set<String> AUTH_PROTOS = new HashSet<String>();
-  static {
-    AUTH_PROTOS.add("http");
-    AUTH_PROTOS.add("https");
-    AUTH_PROTOS.add("ssh");
-  }
-
   public String getRepositoryUrlWithCredentials() {
-    if (isRequireCredentials()) {
-      if (containsCredentials(myRepository))
-        return myRepository;
-      try {
-        return createURLWithCredentials(myRepository);
-      } catch (MalformedURLException e) {
-        Loggers.VCS.warn("Error while parsing url " + myRepository, e);
-      }
-      return myRepository;
-    } else {
-      return myRepository;
-    }
-  }
-
-  private boolean containsCredentials(final String repository) {
-    try {
-      URL url = new URL(null, repository, new FakeStreamHandler());
-      String userInfo = url.getUserInfo();
-      return userInfo != null && userInfo.contains(":");
-    } catch (MalformedURLException e) {
-      return false;
-    }
-  }
-
-  private String createURLWithCredentials(final String originalUrl) throws MalformedURLException {
-    String userInfo = createUserInfo();
-    if (!"".equals(userInfo)) {
-      URL url = new URL(null, originalUrl, new FakeStreamHandler());
-      return url.getProtocol() + "://"
-              + userInfo + "@"
-              + url.getHost()
-              + (url.getPort() != -1 ? ":" + url.getPort() : "")
-              + url.getFile()
-              + (url.getRef() != null ? url.getRef() : "");
-    } else {
-      return originalUrl;
-    }
-  }
-
-  private boolean isRequireCredentials() {
-    for (String scheme : AUTH_PROTOS) {
-      if (myRepository.startsWith(scheme + ":")) {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  private String createUserInfo() {
-    String userInfo = "";
-    if (!StringUtil.isEmpty(myUsername)) {
-      userInfo += myUsername;
-      if (!StringUtil.isEmpty(myPassword)) {
-        userInfo += ":" + myPassword;
-      }
-    }
-    return getEscapedUserInfo(userInfo);
-  }
-
-  private static String getEscapedUserInfo(String userInfo) {
-    try {
-      URI uri = new URI("http", userInfo, "somewhere.com", 80, "", "", "");
-      String escapedURI = uri.toASCIIString();
-      int from = "http://".length();
-      int to = escapedURI.indexOf("somewhere.com") - 1;
-      String escapedUserInfo = escapedURI.substring(from, to);
-      escapedUserInfo = escapedUserInfo.replaceAll("&", "%26");
-      return escapedUserInfo;
-   } catch (URISyntaxException e) {
-      assert false;
-    }
-    return userInfo;
+    return myAuthSettings.getRepositoryUrlWithCredentials(myRepository);
   }
 
   /**
@@ -216,15 +135,12 @@
     return myCustomWorkingDir;
   }
 
+  public AuthSettings getAuthSettings() {
+    return myAuthSettings;
+  }
+
   public static boolean isValidRepository(File dir) {
     // need better way to check that repository copy is ok
     return dir.isDirectory() && new File(dir, ".hg").isDirectory();
   }
-
-  private class FakeStreamHandler extends URLStreamHandler {
-    @Override
-    protected URLConnection openConnection(URL u) throws IOException {
-      throw new UnsupportedOperationException();
-    }
-  }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -27,19 +27,31 @@
   private String myFromId;
   private String myToId;
 
-  public StatusCommand(@NotNull Settings settings, @NotNull File workingDir) {
-    super(settings, workingDir);
+  public StatusCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
   }
 
-  public void setFromRevId(final String fromId) {
-    myFromId = fromId;
+  public StatusCommand fromRevision(@NotNull ChangeSet fromCset) {
+    myFromId = fromCset.getId();
+    return this;
   }
 
-  public void setToRevId(final String toId) {
-    myToId = toId;
+  public StatusCommand fromRevision(@NotNull String fromRevision) {
+    myFromId = new ChangeSet(fromRevision).getId();
+    return this;
   }
 
-  public List<ModifiedFile> execute() throws VcsException {
+  public StatusCommand toRevision(@NotNull ChangeSet toCset) {
+    myToId = toCset.getId();
+    return this;
+  }
+
+  public StatusCommand toRevision(@NotNull String toRevision) {
+    myToId = new ChangeSet(toRevision).getId();
+    return this;
+  }
+
+  public List<ModifiedFile> call() throws VcsException {
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("status");
     cli.addParameter("--rev");
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/TagCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/TagCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -18,6 +18,7 @@
 import com.intellij.execution.configurations.GeneralCommandLine;
 import jetbrains.buildServer.vcs.VcsException;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
 
@@ -26,23 +27,26 @@
   private String myRevId;
   private String myUsername;
 
-  public TagCommand(@NotNull Settings settings, @NotNull File workingDir) {
-    super(settings, workingDir);
-  }
-
-  public void setTag(@NotNull final String tag) {
-    myTag = tag;
+  public TagCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir, authSettings);
   }
 
-  public void setRevId(@NotNull final String revId) {
-    myRevId = revId;
+  public TagCommand tagName(@NotNull String tagName) {
+    myTag = tagName;
+    return this;
   }
 
-  public void setUser(@NotNull final String username) {
-    myUsername = username;
+  public TagCommand revision(@NotNull String revision) {
+    myRevId = new ChangeSet(revision).getId();
+    return this;
   }
 
-  public void execute() throws VcsException {
+  public TagCommand byUser(@Nullable final String username) {
+    myUsername = username;
+    return this;
+  }
+
+  public void call() throws VcsException {
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("tag");
     setUser(cli);
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnknownFileException.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +0,0 @@
-package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
-
-import jetbrains.buildServer.vcs.VcsException;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * @author dmitry.neverov
- */
-public class UnknownFileException extends VcsException {
-
-  private final String myPath;
-
-  public UnknownFileException(@NotNull String path) {
-    super("Unknown file " + path);
-    myPath = path;
-  }
-
-  public String getPath() {
-    return myPath;
-  }
-
-}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnknownRevisionException.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
-
-import jetbrains.buildServer.vcs.VcsException;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * @author dmitry.neverov
- */
-public class UnknownRevisionException extends VcsException {
-
-  private final String myRevision;
-
-  public UnknownRevisionException(@NotNull final String revision) {
-    super("Unknown revision " + revision);
-    myRevision = revision;
-  }
-
-  @NotNull
-  public String getRevision() {
-    return myRevision;
-  }
-
-}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnrelatedRepositoryException.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
-
-import jetbrains.buildServer.vcs.VcsException;
-
-/**
- * @author dmitry.neverov
- */
-public class UnrelatedRepositoryException extends VcsException {
-
-}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -20,8 +20,6 @@
 import org.jetbrains.annotations.NotNull;
 
 import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
 
 import static com.intellij.openapi.util.io.FileUtil.delete;
 import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with;
@@ -31,32 +29,38 @@
   private static final int UPDATE_TIMEOUT_SECONDS = 8 * 3600;//8 hours
 
   private String myToId;
-  private final Map<String, String> myConfigParams = new HashMap<String, String>();
+  private String myBranchName;
 
-  public UpdateCommand(@NotNull Settings settings, @NotNull File workingDir) {
-    super(settings, workingDir);
+  public UpdateCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir,authSettings);
   }
 
-  public void setToId(final String toId) {
-    myToId = toId;
-  }
-
-  public UpdateCommand withConfig(@NotNull String paramName, @NotNull String paramValue) {
-    myConfigParams.put(paramName, paramValue);
+  public UpdateCommand branch(@NotNull String branchName) {
+    myBranchName = branchName;
     return this;
   }
 
-  public void execute() throws VcsException {
+  public UpdateCommand toRevision(@NotNull String revision) {
+    return toRevision(new ChangeSet(revision));
+  }
+
+  public UpdateCommand toRevision(@NotNull ChangeSet cset) {
+    myToId = cset.getId();
+    return this;
+  }
+
+  public void call() throws VcsException {
     ensureWorkingDirIsNotLocked();
+
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("update");
-    addConfigParams(cli);
+    addAuthConfigParams(cli);
     cli.addParameter("-C");
     cli.addParameter("-r");
     if (myToId != null) {
       cli.addParameter(myToId);
     } else {
-      cli.addParameter(getSettings().getBranchName());
+      cli.addParameter(myBranchName);
     }
     runCommand(cli, with().timeout(UPDATE_TIMEOUT_SECONDS));
   }
@@ -67,11 +71,15 @@
       delete(lock);
   }
 
-  private void addConfigParams(GeneralCommandLine cmd) {
-    for (Map.Entry<String, String> entry : myConfigParams.entrySet()) {
-      cmd.addParameter("--config");
-      cmd.addParameter(entry.getKey() + "=" + entry.getValue());
-    }
+  private void addAuthConfigParams(GeneralCommandLine cmd) {
+    if (myAuthSettings == null)
+      return;
+    if (myAuthSettings.getUsername() == null || myAuthSettings.getPassword() == null)
+      return;
+    cmd.addParameters("--config", "auth.tc.prefix=*");
+    cmd.addParameters("--config", "auth.tc.username=" + myAuthSettings.getUsername());
+    cmd.addParameters("--config", "auth.tc.password=" + myAuthSettings.getPassword());
+    cmd.addParameters("--config", "auth.tc.schemes=http https");
   }
 
   @NotNull
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -15,18 +15,14 @@
  */
 public class VcsRootCommand extends BaseCommand {
 
-  private final Settings mySettings;
+  protected final AuthSettings myAuthSettings;
 
-  public VcsRootCommand(@NotNull final Settings settings, @NotNull final File workDir) {
-    super(settings.getHgCommandPath(), workDir);
-    mySettings = settings;
+  public VcsRootCommand(@NotNull String hgPath, @NotNull File workingDir, @NotNull AuthSettings authSettings) {
+    super(hgPath, workingDir);
+    myAuthSettings = authSettings;
   }
 
 
-  public Settings getSettings() {
-    return mySettings;
-  }
-
   protected CommandResult runCommand(@NotNull GeneralCommandLine cli) throws VcsException {
     return CommandUtil.runCommand(cli, with());
   }
@@ -36,7 +32,7 @@
   }
 
   protected Set<String> getPrivateData() {
-    String password = mySettings.getPassword();
+    String password = myAuthSettings.getPassword();
     return password != null ? Collections.singleton(password) : Collections.<String>emptySet();
   }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -2,6 +2,7 @@
 
 import com.intellij.execution.configurations.GeneralCommandLine;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.HgVersion;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.ParseHgVersionException;
 import jetbrains.buildServer.vcs.VcsException;
 import org.jetbrains.annotations.NotNull;
 
@@ -22,7 +23,7 @@
   }
 
 
-  public HgVersion execute() throws VcsException, ParseHgVersionException {
+  public HgVersion call() throws VcsException, ParseHgVersionException {
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("version");
     cli.addParameter("--quiet");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/ConnectionRefusedException.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,14 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception;
+
+import jetbrains.buildServer.vcs.VcsException;
+
+/**
+ * @author dmitry.neverov
+ */
+public class ConnectionRefusedException extends VcsException {
+
+  public ConnectionRefusedException() {
+    super("Connection refused");
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/ParseHgVersionException.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,15 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception;
+
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author dmitry.neverov
+ */
+public class ParseHgVersionException extends VcsException {
+
+  public ParseHgVersionException(@NotNull String version) {
+    super("Cannot parse version '" + version + "'");
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/UnknownFileException.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,22 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception;
+
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author dmitry.neverov
+ */
+public class UnknownFileException extends VcsException {
+
+  private final String myPath;
+
+  public UnknownFileException(@NotNull String path) {
+    super("Unknown file " + path);
+    myPath = path;
+  }
+
+  public String getPath() {
+    return myPath;
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/UnknownRevisionException.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,23 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception;
+
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author dmitry.neverov
+ */
+public class UnknownRevisionException extends VcsException {
+
+  private final String myRevision;
+
+  public UnknownRevisionException(@NotNull final String revision) {
+    super("Unknown revision " + revision);
+    myRevision = revision;
+  }
+
+  @NotNull
+  public String getRevision() {
+    return myRevision;
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/UnrelatedRepositoryException.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,10 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception;
+
+import jetbrains.buildServer.vcs.VcsException;
+
+/**
+ * @author dmitry.neverov
+ */
+public class UnrelatedRepositoryException extends VcsException {
+
+}
--- a/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml	Fri Mar 02 14:24:58 2012 +0400
@@ -4,7 +4,7 @@
 <beans default-autowire="constructor">
   <bean id="mercurialServer" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialVcsSupport" />
   <bean id="config" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerPluginConfigImpl" />
-  <bean id="commandFactory" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.CommandFactoryImpl" />
+  <bean id="commandFactory" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.RepoFactory" />
   <bean id="hgPathProvider" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerHgPathProvider"/>
   <bean id="mirrorManager" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MirrorManagerImpl" />
 </beans>
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesCommand.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-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;
-
-}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesNoRevsets.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,83 +0,0 @@
-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);
-      }
-      Collections.reverse(result);
-      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;
-  }
-}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesWithRevsets.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-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	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-package jetbrains.buildServer.buildTriggers.vcs.mercurial;
-
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.LogCommand;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.MergeBaseCommand;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
-import jetbrains.buildServer.vcs.VcsException;
-import org.jetbrains.annotations.NotNull;
-
-import java.io.File;
-
-/**
- * @author dmitry.neverov
- */
-public interface CommandFactory {
-
-  @NotNull
-  public MergeBaseCommand createMergeBase(@NotNull Settings settings, @NotNull File workingDir) throws VcsException;
-
-  @NotNull
-  public LogCommand createLog(@NotNull final Settings settings, @NotNull final File workingDir);
-
-  @NotNull
-  public CollectChangesCommand getCollectChangesCommand(@NotNull final Settings settings, @NotNull final File workingDir) throws VcsException;
-
-}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandFactoryImpl.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-package jetbrains.buildServer.buildTriggers.vcs.mercurial;
-
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.LogCommand;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.MergeBaseCommand;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.VersionCommand;
-import jetbrains.buildServer.util.FileUtil;
-import jetbrains.buildServer.vcs.VcsException;
-import org.jetbrains.annotations.NotNull;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * @author dmitry.neverov
- */
-public final class CommandFactoryImpl implements CommandFactory {
-
-  //hg version which supports revsets
-  private final static HgVersion REVSET_HG_VERSION = new HgVersion(1, 7, 0);
-  private final static String LOG_TEMPLATE_NAME = "log.template";
-
-  private final File myDefaultWorkingDir;
-  private final File myLogTemplate;
-
-
-  public CommandFactoryImpl(@NotNull final ServerPluginConfig config) throws IOException {
-    myDefaultWorkingDir = config.getCachesDir();
-    myLogTemplate = createLogTemplate(config.getPluginDataDir());
-  }
-
-
-  @NotNull
-  public MergeBaseCommand createMergeBase(@NotNull Settings settings, @NotNull File workingDir) throws VcsException {
-    HgVersion hgVersion = getHgVersion(settings);
-    if (hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION))
-      return new MergeBaseWithRevsets(settings, workingDir, this);
-    else
-      return new MergeBaseNoRevsets(settings, workingDir, this);
-  }
-
-
-  @NotNull
-  public LogCommand createLog(@NotNull final Settings settings, @NotNull final File workingDir) {
-    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);
-    FileUtil.copyResource(CommandFactoryImpl.class, "/buildServerResources/log.template", template);
-    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	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Fri Mar 02 14:24:58 2012 +0400
@@ -19,6 +19,8 @@
 import jetbrains.buildServer.Used;
 import jetbrains.buildServer.buildTriggers.vcs.AbstractVcsPropertiesProcessor;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnknownRevisionException;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnrelatedRepositoryException;
 import jetbrains.buildServer.log.Loggers;
 import jetbrains.buildServer.serverSide.*;
 import jetbrains.buildServer.util.EventDispatcher;
@@ -37,8 +39,6 @@
 import java.io.IOException;
 import java.util.*;
 
-import static com.intellij.openapi.util.text.StringUtil.isEmptyOrSpaces;
-
 /**
  * Mercurial VCS plugin for TeamCity works as follows:
  * <ul>
@@ -57,7 +57,7 @@
   private final MirrorManager myMirrorManager;
   private final ServerPluginConfig myConfig;
   private final HgPathProvider myHgPathProvider;
-  private final CommandFactory myCommandFactory;
+  private final RepoFactory myRepoFactory;
   private final FileFilter myIgnoreDotHgFilter = new IgnoreDotHgFilter();
   private final FileFilter myAcceptAllFilter = new AcceptAllFilter();
 
@@ -67,14 +67,14 @@
                              @NotNull final ResetCacheRegister resetCacheHandlerManager,
                              @NotNull final ServerPluginConfig config,
                              @NotNull final HgPathProvider hgPathProvider,
-                             @NotNull final CommandFactory commandFactory,
+                             @NotNull final RepoFactory repoFactory,
                              @NotNull final MirrorManager mirrorManager) {
     myVcsManager = vcsManager;
     myConfig = config;
     myDefaultWorkFolderParent = myConfig.getCachesDir();
     myMirrorManager = mirrorManager;
     myHgPathProvider = hgPathProvider;
-    myCommandFactory = commandFactory;
+    myRepoFactory = repoFactory;
     resetCacheHandlerManager.registerHandler(new MercurialResetCacheHandler(myMirrorManager));
     dispatcher.addListener(new BuildServerAdapter() {
       @Override
@@ -161,10 +161,8 @@
     ChangeSet cset = new ChangeSet(version);
     Settings settings = createSettings(vcsRoot);
     syncRepository(settings, cset);
-    File workingDir = getWorkingDir(settings);
-    CatCommand cc = new CatCommand(settings, workingDir);
-    cc.setRevId(cset.getId());
-    File parentDir = cc.execute(Collections.singletonList(filePath));
+    HgRepo repo = createRepo(settings);
+    File parentDir = repo.cat().files(filePath).atRevision(cset).call();
     File file = new File(parentDir, filePath);
     try {
       return FileUtil.loadFileBytes(file);
@@ -211,13 +209,11 @@
   public String getCurrentVersion(@NotNull final VcsRoot root) throws VcsException {
     Settings settings = createSettings(root);
     syncRepository(settings);
-    File workingDir = getWorkingDir(settings);
-    BranchesCommand branches = new BranchesCommand(settings, workingDir);
-    Map<String, ChangeSet> result = branches.execute();
+    HgRepo repo = createRepo(settings);
+    Map<String, ChangeSet> result = repo.branches().call();
     if (!result.containsKey(settings.getBranchName())) {
       throw new VcsException("Unable to find current version for the branch: " + settings.getBranchName());
     }
-
     return result.get(settings.getBranchName()).getFullVersion();
   }
 
@@ -235,14 +231,15 @@
     return new TestConnectionSupport() {
       public String testConnection(@NotNull final VcsRoot vcsRoot) throws VcsException {
         Settings settings = createSettings(vcsRoot);
-        File workingDir = getWorkingDir(settings);
-        IdentifyCommand id = new IdentifyCommand(settings, workingDir);
+        String idResult = createRepo(settings).id()
+                .repository(settings.getRepository())
+                .withAuthSettings(settings.getAuthSettings())
+                .call();
         StringBuilder res = new StringBuilder();
         res.append(quoteIfNeeded(settings.getHgCommandPath()));
         res.append(" identify ");
-        final String obfuscatedUrl = CommandUtil.removePrivateData(settings.getRepositoryUrlWithCredentials(), Collections.singleton(settings.getPassword()));
-        res.append(quoteIfNeeded(obfuscatedUrl));
-        res.append('\n').append(id.execute());
+        res.append(quoteIfNeeded(settings.getAuthSettings().getRepositoryUrlWithHiddenPassword(settings.getRepository())));
+        res.append('\n').append(idResult);
         return res.toString();
       }
     };
@@ -252,7 +249,6 @@
     if (str.indexOf(' ') != -1) {
       return "\"" + str + "\"";
     }
-
     return str;
   }
 
@@ -287,11 +283,8 @@
   // builds patch from version to version
   private void buildIncrementalPatch(final Settings settings, @NotNull final ChangeSet fromVer, @NotNull final ChangeSet toVer, final PatchBuilder builder, final CheckoutRules checkoutRules)
     throws VcsException, IOException {
-    File workingDir = getWorkingDir(settings);
-    StatusCommand st = new StatusCommand(settings, workingDir);
-    st.setFromRevId(fromVer.getId());
-    st.setToRevId(toVer.getId());
-    List<ModifiedFile> modifiedFiles = st.execute();
+    HgRepo repo = createRepo(settings);
+    List<ModifiedFile> modifiedFiles = repo.status().fromRevision(fromVer).toRevision(toVer).call();
     List<String> notDeletedFiles = new ArrayList<String>();
     for (ModifiedFile f: modifiedFiles) {
       if (f.getStatus() != ModifiedFile.Status.REMOVED) {
@@ -299,12 +292,10 @@
       }
     }
 
-    if (notDeletedFiles.isEmpty()) return;
+    if (notDeletedFiles.isEmpty())
+      return;
 
-    CatCommand cc = new CatCommand(settings, workingDir);
-    cc.setRevId(toVer.getId());
-    File parentDir = cc.execute(notDeletedFiles);
-
+    File parentDir = repo.cat().files(notDeletedFiles).atRevision(toVer).call();
     try {
       for (ModifiedFile f: modifiedFiles) {
         String mappedPath = checkoutRules.map(f.getPath());
@@ -339,27 +330,22 @@
     throws IOException, VcsException {
     File tempDir = FileUtil.createTempDirectory("mercurial", toVer.getId());
     try {
-      File mirrorDir = getWorkingDir(settings);
-      if (hasSubrepositories(settings, toVer)) {
+      HgRepo repo = createRepo(settings);
+      if (repo.hasSubreposAtRevision(toVer)) {
         Loggers.VCS.debug("Repository '" + settings.getRepository() + "' has submodules at revision " + toVer.getId() + ", use 'hg clone' to build clean patch");
-        CloneCommand cl = new CloneCommand(settings, tempDir);
-        cl.setRepository(mirrorDir.getAbsolutePath());
-        cl.setToId(toVer.getId());
-        cl.setUpdateWorkingDir(false);
-        cl.setUsePullProtocol(myConfig.isUsePullProtocol());
-        cl.execute();
-
-        UpdateCommand up = new UpdateCommand(settings, tempDir);
-        up.setToId(toVer.getId());
-        up.execute();
-
+        File mirrorDir = getWorkingDir(settings);
+        HgRepo cloneOfTheMirror = createRepo(settings, tempDir);
+        cloneOfTheMirror.doClone().fromRepository(mirrorDir)
+                .setUpdateWorkingDir(false)
+                .setUsePullProtocol(false)
+                .useUncompressedTransfer(false)
+                .call();
+        cloneOfTheMirror.setDefaultPath(settings.getRepository());
+        cloneOfTheMirror.update().toRevision(toVer).call();
         buildPatchFromDirectory(builder, tempDir, checkoutRules, myIgnoreDotHgFilter);
       } else {
         Loggers.VCS.debug("Repository '" + settings.getRepository() + "' doesn't have submodules at revision " + toVer.getId() + ", use 'hg archive' to build clean patch");
-        ArchiveCommand archive = new ArchiveCommand(settings, mirrorDir);
-        archive.setDestDir(tempDir);
-        archive.setToId(toVer.getId());
-        archive.execute();
+        repo.archive().revision(toVer).toDir(tempDir).call();
         buildPatchFromDirectory(builder, tempDir, checkoutRules, myAcceptAllFilter);
       }
     } finally {
@@ -367,38 +353,6 @@
     }
   }
 
-  private boolean hasSubrepositories(@NotNull final Settings settings, @NotNull final ChangeSet cset) throws VcsException {
-    String hgsub = getFileContent(".hgsub", settings, cset);
-    return !isEmptyOrSpaces(hgsub);
-  }
-
-  /**
-   * Returns file's content or empty string if it doesn't exist.
-   * @param path path of the file of interest
-   * @param settings root settings
-   * @param cset repository cset (should be present in the repository)
-   * @return see above
-   */
-  @NotNull
-  private String getFileContent(@NotNull final String path, @NotNull final Settings settings, @NotNull final ChangeSet cset) throws VcsException {
-    File dir = getWorkingDir(settings);
-    CatCommand cat = new CatCommand(settings, dir);
-    cat.setRevId(cset.getId());
-    File parentDir = cat.execute(Collections.singletonList(path), false);
-    try {
-      File f = new File(parentDir, path);
-      if (f.isFile())
-        return FileUtil.readText(f);
-      else
-        return "";
-    } catch (Exception e) {
-      return "";
-    } finally {
-      deleteTmpDir(parentDir);
-    }
-  }
-
-
   private void buildPatchFromDirectory(final PatchBuilder builder, final File repRoot, final CheckoutRules checkoutRules, @NotNull final FileFilter filter) throws IOException {
     buildPatchFromDirectory(repRoot, builder, repRoot, checkoutRules, filter);
   }
@@ -431,28 +385,29 @@
     }
   }
 
-  /**
-   * clone the repo if it doesn't exist, pull the repo if it doesn't contain specified changeSet
-   */
+  /* clone the repo if it doesn't exist, pull the repo if it doesn't contain specified changeSet */
   private void syncRepository(final Settings settings, final ChangeSet cset) throws VcsException {
     File workingDir = getWorkingDir(settings);
     lockWorkDir(workingDir);
+    HgRepo repo = createRepo(settings);
     try {
-      if (Settings.isValidRepository(workingDir)) {
-        if (!isChangeSetExist(settings, workingDir, cset)) {
-          try {
-            PullCommand pull = new PullCommand(settings, workingDir);
-            pull.execute(myConfig.getPullTimeout());
-          } catch (UnrelatedRepositoryException e) {
-            Loggers.VCS.warn("Repository at " + settings.getRepository() + " is unrelated, clone it again");
-            myMirrorManager.forgetDir(workingDir);
-            syncRepository(settings, cset);
-          }
+      if (repo.isValidRepository()) {
+        if (repo.containsRevision(cset))
+          return;
+        try {
+          repo.pull().fromRepository(settings.getRepository())
+                  .withTimeout(myConfig.getPullTimeout())
+                  .call();
+        } catch (UnrelatedRepositoryException e) {
+          Loggers.VCS.warn("Repository at " + settings.getRepository() + " is unrelated, clone it again");
+          myMirrorManager.forgetDir(workingDir);
+          syncRepository(settings, cset);
         }
       } else {
-        CloneCommand cl = new CloneCommand(settings, workingDir);
-        cl.setUpdateWorkingDir(false);
-        cl.execute();
+        repo.doClone().fromRepository(settings.getRepository())
+                .useUncompressedTransfer(settings.isUncompressedTransfer())
+                .setUpdateWorkingDir(false)
+                .call();
       }
     } finally {
       unlockWorkDir(workingDir);
@@ -462,45 +417,29 @@
   private void syncRepository(final Settings settings) throws VcsException {
     File workingDir = getWorkingDir(settings);
     lockWorkDir(workingDir);
+    HgRepo repo = createRepo(settings);
     try {
-      if (Settings.isValidRepository(workingDir)) {
+      if (repo.isValidRepository()) {
         try {
-          PullCommand pull = new PullCommand(settings, workingDir);
-          pull.execute(myConfig.getPullTimeout());
+          repo.pull().fromRepository(settings.getRepository())
+                  .withTimeout(myConfig.getPullTimeout())
+                  .call();
         } catch (UnrelatedRepositoryException e) {
           Loggers.VCS.warn("Repository at " + settings.getRepository() + " is unrelated, clone it again");
           myMirrorManager.forgetDir(workingDir);
           syncRepository(settings);
         }
       } else {
-        CloneCommand cl = new CloneCommand(settings, workingDir);
-        cl.setUpdateWorkingDir(false);
-        cl.execute();
+        repo.doClone().fromRepository(settings.getRepository())
+                .setUpdateWorkingDir(false)
+                .useUncompressedTransfer(settings.isUncompressedTransfer())
+                .call();
       }
     } finally {
       unlockWorkDir(workingDir);
     }
   }
 
-  /**
-   * Check if changeSet is present in local repository.
-   * @param settings root settings
-   * @param workDir where to run a command
-   * @param cset change set of interest
-   * @return true if changeSet is present in local repository
-   */
-  private boolean isChangeSetExist(Settings settings, File workDir, ChangeSet cset) {
-    try {
-      IdentifyCommand identify = new IdentifyCommand(settings, workDir);
-      identify.setInLocalRepository(true);
-      identify.setChangeSet(cset);
-      identify.execute();
-      return true;
-    } catch (VcsException e) {
-      return false;
-    }
-  }
-
   @Override
   public LabelingSupport getLabelingSupport() {
     return this;
@@ -525,10 +464,9 @@
   private Map<String, String> getBranchesRevisions(@NotNull VcsRoot root) throws VcsException {
     Settings settings = createSettings(root);
     syncRepository(settings);
-    File workingDir = getWorkingDir(settings);
-    BranchesCommand branches = new BranchesCommand(settings, workingDir);
+    HgRepo repo = createRepo(settings);
     Map<String, String> result = new HashMap<String, String>();
-    for (Map.Entry<String, ChangeSet> entry : branches.execute().entrySet()) {
+    for (Map.Entry<String, ChangeSet> entry : repo.branches().call().entrySet()) {
       result.put(entry.getKey(), entry.getValue().getId());
     }
     return result;
@@ -553,11 +491,11 @@
     if (mergeBase == null)
       return null;
 
-    LogCommand lc = myCommandFactory.createLog(settings, getWorkingDir(settings));
-    lc.setFromRevId(new ChangeSetRevision(mergeBase).getId());
-    lc.setToRevId(new ChangeSetRevision(branchVersion).getId());
-    lc.showCommitsFromAllBranches();
-    List<ChangeSet> changeSets = lc.execute();
+    List<ChangeSet> changeSets = createRepo(settings).log()
+            .fromRevision(mergeBase)
+            .toRevision(branchVersion)
+            .showCommitsFromAllBranches()
+            .call();
     if (changeSets.size() > 1) {//when branch points to the commit in original branch we get 1 cset
       String branchId = changeSets.get(1).getId();
       String username = changeSets.get(changeSets.size() - 1).getUser();
@@ -590,7 +528,10 @@
 
   @Nullable
   private String getMergeBase(@NotNull Settings settings, @NotNull String revision1, @NotNull String revision2) throws VcsException {
-    String result = myCommandFactory.createMergeBase(settings, getWorkingDir(settings)).execute(revision1, revision2);
+    String result = createRepo(settings).mergeBase()
+            .revision1(revision1)
+            .revision2(revision2)
+            .call();
     if (result == null)
       result = getMinusNthCommit(settings, 10);
     return result;
@@ -599,11 +540,12 @@
 
   @Nullable
   private String getMinusNthCommit(@NotNull Settings settings, int n) throws VcsException {
-    LogCommand log = myCommandFactory.createLog(settings, getWorkingDir(settings));
-    log.setToRevId(settings.getBranchName());
+    LogCommand log = createRepo(settings).log()
+            .inBranch(settings.getBranchName())
+            .toNamedRevision(settings.getBranchName());
     if (n > 0)
       log.setLimit(n);
-    List<ChangeSet> changeSets = log.execute();
+    List<ChangeSet> changeSets = log.call();
     if (changeSets.isEmpty())
       return null;
     return changeSets.get(0).getId();
@@ -647,10 +589,11 @@
       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);
     try {
-      List<ChangeSet> changesets = log.execute(fromCommit, toCommit);
+      List<ChangeSet> changesets = createRepo(settings).collectChanges()
+              .fromRevision(fromCommit)
+              .toRevision(toCommit)
+              .call();
       Iterator<ChangeSet> iter = changesets.iterator();
       while (iter.hasNext()) {
         ChangeSet cset = iter.next();
@@ -706,20 +649,16 @@
       Settings settings = createSettings(root);
       settings.setCustomWorkingDir(tmpDir);
       syncRepository(settings);
-      File workingDir = getWorkingDir(settings);
-      new UpdateCommand(settings, workingDir).execute();
+      HgRepo repo = createRepo(settings);
+      repo.update().branch(settings.getBranchName()).call();
 
       String fixedTagname = fixTagName(label);
-      TagCommand tc = new TagCommand(settings, workingDir);
-      tc.setRevId(new ChangeSet(version).getId());
-      tc.setTag(fixedTagname);
-      String user = settings.getUserForTag();
-      if (user != null)
-        tc.setUser(user);
-      tc.execute();
+      repo.tag().revision(version)
+              .tagName(fixedTagname)
+              .byUser(settings.getUserForTag())
+              .call();
 
-      PushCommand pc = new PushCommand(settings, workingDir);
-      pc.execute();
+      repo.push().toRepository(settings.getRepository()).call();
       return fixedTagname;
     } finally {
       if (tmpDir != null)
@@ -805,6 +744,14 @@
     }
   }
 
+  private ServerHgRepo createRepo(@NotNull Settings s) {
+    return myRepoFactory.create(getWorkingDir(s), s.getHgCommandPath(), s.getAuthSettings());
+  }
+
+  private HgRepo createRepo(@NotNull Settings s, @NotNull File customDir) {
+    return myRepoFactory.create(customDir, s.getHgCommandPath(), s.getAuthSettings());
+  }
+
   @NotNull
   public String getBranchName(@NotNull final VcsRoot root) {
     try {
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseNoRevsets.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-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.DAGs;
-import jetbrains.buildServer.vcs.VcsException;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.io.File;
-import java.util.*;
-
-/**
- * Implementation of merge-base for hg versions which don't have revsets
- * @author dmitry.neverov
- */
-public final class MergeBaseNoRevsets implements MergeBaseCommand {
-
-  private final Settings mySettings;
-  private final File myWorkingDir;
-  private final CommandFactoryImpl myCommandFactory;
-
-  public MergeBaseNoRevsets(@NotNull final Settings settings, @NotNull final File workingDir, @NotNull final CommandFactoryImpl commandFactory) {
-    mySettings = settings;
-    myWorkingDir = workingDir;
-    myCommandFactory = commandFactory;
-  }
-
-
-  @Nullable
-  public String execute(@NotNull final String revision1, @NotNull final String revision2) {
-    if (revision1.equals(revision2))
-      return revision1;
-    try {
-      List<Pair<String, String>> edges = new ArrayList<Pair<String, String>>();
-      fillEdges(edges, getRevisionsReachableFrom(revision1));
-      fillEdges(edges, getRevisionsReachableFrom(revision2));
-      DAG<String> dag = DAGs.createFromEdges(edges);
-      List<String> commonAncestors = dag.getCommonAncestors(new ChangeSetRevision(revision1).getId(), new ChangeSetRevision(revision2).getId());
-      return commonAncestors.isEmpty() ? null : commonAncestors.get(0);
-    } catch (VcsException e) {
-      return null;
-    }
-  }
-
-
-  private List<ChangeSet> getRevisionsReachableFrom(@NotNull final String revision) throws VcsException {
-    LogCommand log = myCommandFactory.createLog(mySettings, myWorkingDir);
-    log.setFromRevId(new ChangeSetRevision(revision).getId());
-    log.showCommitsFromAllBranches();
-    log.setToRevId("0");
-    return log.execute();
-  }
-
-
-  private void fillEdges(List<Pair<String, String>> edges, List<ChangeSet> csets) {
-    for (ChangeSet cset : csets) {
-      for (ChangeSetRevision parent : cset.getParents()) {
-        edges.add(Pair.create(cset.getId(), parent.getId()));
-      }
-    }
-  }
-}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseWithRevsets.java	Thu Mar 01 20:15:42 2012 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-package jetbrains.buildServer.buildTriggers.vcs.mercurial;
-
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
-import jetbrains.buildServer.vcs.VcsException;
-import org.jetbrains.annotations.NotNull;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * Implementation of merge-base using hg revsets
- * @author dmitry.neverov
- */
-public final class MergeBaseWithRevsets implements MergeBaseCommand {
-
-  private final Settings mySettings;
-  private final File myWorkingDir;
-  private final CommandFactoryImpl myCommandFactory;
-
-  public MergeBaseWithRevsets(@NotNull final Settings settings, @NotNull final File workingDir, @NotNull final CommandFactoryImpl commandFactory) {
-    mySettings = settings;
-    myWorkingDir = workingDir;
-    myCommandFactory = commandFactory;
-  }
-
-  public String execute(@NotNull final String revision1, @NotNull final String revision2) throws VcsException {
-    try {
-      LogCommand log = myCommandFactory.createLog(mySettings, myWorkingDir);
-      log.setRevsets("ancestor(" + new ChangeSetRevision(revision1).getId() + ", " + new ChangeSetRevision(revision2).getId() + ")");
-      log.showCommitsFromAllBranches();
-      log.setCalculateParents(false);
-      List<ChangeSet> csets = log.execute();
-      return csets.isEmpty() ? null : csets.get(0).getId();
-    } catch (VcsException e) {
-      return null;
-    }
-  }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/RepoFactory.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,32 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.AuthSettings;
+import jetbrains.buildServer.util.FileUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * @author dmitry.neverov
+ */
+public final class RepoFactory {
+
+  private final static String LOG_TEMPLATE_NAME = "log.template";
+  private final File myLogTemplate;
+
+  public RepoFactory(@NotNull ServerPluginConfig config) throws IOException {
+    myLogTemplate = createLogTemplate(config.getPluginDataDir());
+  }
+
+  @NotNull
+  public ServerHgRepo create(@NotNull File workingDir, @NotNull String hgPath, @NotNull AuthSettings authSettings) {
+    return new ServerHgRepo(workingDir, hgPath, authSettings).withLogTemplate(myLogTemplate);
+  }
+
+  private File createLogTemplate(@NotNull final File templateFileDir) throws IOException {
+    File template = new File(templateFileDir, LOG_TEMPLATE_NAME);
+    FileUtil.copyResource(RepoFactory.class, "/buildServerResources/log.template", template);
+    return template;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgRepo.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,52 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+
+/**
+ * @author dmitry.neverov
+ */
+public class ServerHgRepo extends HgRepo {
+
+  private final static HgVersion REVSET_HG_VERSION = new HgVersion(1, 7, 0);
+  private File myLogTemplate;
+
+  public ServerHgRepo(@NotNull File workingDir, @NotNull String hgPath, @NotNull AuthSettings authSettings) {
+    super(workingDir, hgPath, authSettings);
+  }
+
+  public ServerHgRepo withLogTemplate(@NotNull File logTemplate) {
+    myLogTemplate = logTemplate;
+    return this;
+  }
+
+  public LogCommand log() {
+    return new LogCommand(myHgPath, myWorkingDir, myAuthSettings).withTemplate(myLogTemplate);
+  }
+
+  public MergeBaseCommand mergeBase() throws VcsException {
+    HgVersion hgVersion = getHgVersion();
+    if (hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION))
+      return new MergeBaseWithRevsets(this);
+    else
+      return new MergeBaseNoRevsets(this);
+  }
+
+  @NotNull
+  public CollectChangesCommand collectChanges() throws VcsException {
+    HgVersion hgVersion = getHgVersion();
+    if (hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION)) {
+      return new CollectChangesWithRevsets(this);
+    } else {
+      return new CollectChangesNoRevsets(this);
+    }
+  }
+
+  private HgVersion getHgVersion() throws VcsException {
+    VersionCommand versionCommand = new VersionCommand(myHgPath, myWorkingDir);
+    return versionCommand.call();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,32 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author dmitry.neverov
+ */
+public abstract class CollectChangesCommand {
+
+  protected String myFromRevision;
+  protected String myToRevision;
+
+  @NotNull
+  public abstract List<ChangeSet> call(@NotNull String fromCommit, @NotNull String toCommit) throws VcsException;
+
+  public abstract List<ChangeSet> call() throws VcsException;
+
+  public CollectChangesCommand fromRevision(@NotNull String fromRevision) {
+    myFromRevision = fromRevision;
+    return this;
+  }
+
+  public CollectChangesCommand toRevision(@NotNull String toRevision) {
+    myToRevision = toRevision;
+    return this;
+  }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesNoRevsets.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,80 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import com.intellij.openapi.util.Pair;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerHgRepo;
+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.util.*;
+
+/**
+ * @author dmitry.neverov
+ */
+public class CollectChangesNoRevsets extends CollectChangesCommand {
+
+  private ServerHgRepo myRepo;
+
+  public CollectChangesNoRevsets(@NotNull ServerHgRepo repo) {
+    myRepo = repo;
+  }
+
+  @Override
+  public List<ChangeSet> call() throws VcsException {
+    return call(myFromRevision, myToRevision);
+  }
+
+  @NotNull
+  public List<ChangeSet> call(@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);
+      }
+      Collections.reverse(result);
+      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 {
+    return myRepo.log()
+            .fromRevision(revision)
+            .toRevision("0")
+            .showCommitsFromAllBranches()
+            .call();
+  }
+
+
+  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/command/CollectChangesWithRevsets.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,32 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.HgRepo;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author dmitry.neverov
+ */
+public class CollectChangesWithRevsets extends CollectChangesCommand {
+
+  private HgRepo myRepo;
+
+  public CollectChangesWithRevsets(@NotNull HgRepo repo) {
+    myRepo = repo;
+  }
+
+  @Override
+  public List<ChangeSet> call() throws VcsException {
+    return call(myFromRevision, myToRevision);
+  }
+
+  @NotNull
+  public List<ChangeSet> call(@NotNull final String fromCommit, @NotNull final String toCommit) throws VcsException {
+    return myRepo.log()
+            .showCommitsFromAllBranches()
+            .withRevsets("ancestors(" + toCommit + ") - ancestors(" + fromCommit + ")")
+            .call();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeBaseCommand.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,42 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.HgRepo;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Analog of git merge-base. It returns a last common ancestor between two revisions.
+ *
+ * @author dmitry.neverov
+ */
+public abstract class MergeBaseCommand {
+
+  protected final HgRepo myRepo;
+  protected String myRevision1;
+  protected String myRevision2;
+
+  protected MergeBaseCommand(@NotNull HgRepo repo) {
+    myRepo = repo;
+  }
+
+  public MergeBaseCommand revision1(String revision1) {
+    myRevision1 = revision1;
+    return this;
+  }
+
+  public MergeBaseCommand revision2(String revision2) {
+    myRevision2 = revision2;
+    return this;
+  }
+
+  /**
+   * Returns hash of least common ancestor between two revisions or null
+   * if common ancestor is not found
+   * @return see above
+   * @throws VcsException if some commands fail
+   */
+  @Nullable
+  public abstract String call() throws VcsException;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeBaseNoRevsets.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,54 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import com.intellij.openapi.util.Pair;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.HgRepo;
+import jetbrains.buildServer.util.graph.DAG;
+import jetbrains.buildServer.util.graph.DAGs;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of merge-base for hg versions which don't have revsets
+ * @author dmitry.neverov
+ */
+public final class MergeBaseNoRevsets extends MergeBaseCommand {
+
+  public MergeBaseNoRevsets(@NotNull HgRepo repo) {
+    super(repo);
+  }
+
+  public String call() {
+    if (myRevision1.equals(myRevision2))
+      return myRevision1;
+    try {
+      List<Pair<String, String>> edges = new ArrayList<Pair<String, String>>();
+      fillEdges(edges, getRevisionsReachableFrom(myRevision1));
+      fillEdges(edges, getRevisionsReachableFrom(myRevision2));
+      DAG<String> dag = DAGs.createFromEdges(edges);
+      List<String> commonAncestors = dag.getCommonAncestors(new ChangeSetRevision(myRevision1).getId(), new ChangeSetRevision(myRevision2).getId());
+      return commonAncestors.isEmpty() ? null : commonAncestors.get(0);
+    } catch (VcsException e) {
+      return null;
+    }
+  }
+
+  private List<ChangeSet> getRevisionsReachableFrom(@NotNull final String revision) throws VcsException {
+    return myRepo.log()
+            .fromRevision(revision)
+            .toRevision("0")
+            .showCommitsFromAllBranches()
+            .call();
+  }
+
+
+  private void fillEdges(List<Pair<String, String>> edges, List<ChangeSet> csets) {
+    for (ChangeSet cset : csets) {
+      for (ChangeSetRevision parent : cset.getParents()) {
+        edges.add(Pair.create(cset.getId(), parent.getId()));
+      }
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeBaseWithRevsets.java	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,31 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.HgRepo;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * Implementation of merge-base using hg revsets
+ * @author dmitry.neverov
+ */
+public final class MergeBaseWithRevsets extends MergeBaseCommand {
+
+  public MergeBaseWithRevsets(@NotNull HgRepo repo) {
+    super(repo);
+  }
+
+  public String call() throws VcsException {
+    try {
+      List<ChangeSet> csets = myRepo.log()
+              .withRevsets("ancestor(" + new ChangeSetRevision(myRevision1).getId() + ", " + new ChangeSetRevision(myRevision2).getId() + ")")
+              .showCommitsFromAllBranches()
+              .dontCalculateParents()
+              .call();
+      return csets.isEmpty() ? null : csets.get(0).getId();
+    } catch (VcsException e) {
+      return null;
+    }
+  }
+}
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -200,7 +200,7 @@
     assertTrue(hgrcContent.contains("default = " + root.getProperty(Constants.REPOSITORY_PROP)));
   }
 
-  public void new_repository_is_cloned_from_local_mirror() throws IOException, VcsException {
+  private void new_repository_is_cloned_from_local_mirror() throws IOException, VcsException {
     VcsRoot root = createVcsRoot(simpleRepo());
     File workingDir = doUpdate(root, "3:9522278aa38d", new IncludeRule(".", ".", null), true);
     File mirrorDir = FileUtil.getSubDirectories(myMirrorsRootDir).get(0);
@@ -209,7 +209,7 @@
     assertTrue(hgrcContent.contains("default = " + mirrorDir.getCanonicalPath()));
   }
 
-  public void repository_cloned_from_remote_start_cloning_from_local_mirror() throws IOException, VcsException {
+  private void repository_cloned_from_remote_start_cloning_from_local_mirror() throws IOException, VcsException {
     VcsRoot root = createVcsRoot(simpleRepo());
     //clone from remote repository
     File workingDir = doUpdate(root, "3:9522278aa38d", new IncludeRule(".", ".", null));
@@ -222,7 +222,7 @@
     assertTrue(hgrcContent2.contains("default = " + newMirrorDir.getCanonicalPath()));//now it clones from local mirror
   }
 
-  public void repository_cloned_from_local_mirror_start_cloning_from_remote() throws IOException, VcsException {
+  private void repository_cloned_from_local_mirror_start_cloning_from_remote() throws IOException, VcsException {
     VcsRoot root = createVcsRoot(simpleRepo());
     //clone from remote repository
     File workingDir = doUpdate(root, "3:9522278aa38d", new IncludeRule(".", ".", null), true);
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutWithSubreposTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutWithSubreposTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -6,6 +6,8 @@
 import jetbrains.buildServer.agent.BuildAgentConfiguration;
 import jetbrains.buildServer.agent.BuildProgressLogger;
 import jetbrains.buildServer.agent.vcs.UpdateByIncludeRules2;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.AuthSettings;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.ConnectionRefusedException;
 import jetbrains.buildServer.log.Log4jFactory;
 import jetbrains.buildServer.util.FileUtil;
 import jetbrains.buildServer.vcs.CheckoutRules;
@@ -22,7 +24,15 @@
 
 import java.io.File;
 import java.io.IOException;
-import java.util.Collections;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.getHgPath;
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder.vcsRoot;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
 
 /**
  * @author dmitry.neverov
@@ -37,7 +47,9 @@
   private BuildProgressLogger myLogger;
   private UpdateByIncludeRules2 myVcsSupport;
   private int myBuildCounter = 0;
-  private File myR1Dir;
+  private List<Process> myProcesses;
+  private File myDefaultWorkDir;
+  private MirrorManagerImpl myMirrorManager;
 
   static {
     Logger.setFactory(new Log4jFactory());
@@ -45,8 +57,10 @@
 
   @BeforeMethod
   public void setUp() throws Exception {
+    myProcesses = new ArrayList<Process>();
     myOriginalRepositoriesParentDir = myTempFiles.createTempDir();
-    myWorkDir = new File(myOriginalRepositoriesParentDir, "agentWorkDir");
+    myDefaultWorkDir = new File(myOriginalRepositoriesParentDir, "agentWorkDir");
+    myWorkDir = myDefaultWorkDir;
     myContext = new Mockery();
 
     final BuildAgentConfiguration agentConfig = myContext.mock(BuildAgentConfiguration.class);
@@ -56,39 +70,85 @@
     }});
 
     final AgentPluginConfigImpl pluginConfig = new AgentPluginConfigImpl(agentConfig);
-    myVcsSupport = new MercurialAgentSideVcsSupport(pluginConfig, new AgentHgPathProvider(agentConfig), new MirrorManagerImpl(pluginConfig));
+    myMirrorManager = new MirrorManagerImpl(pluginConfig);
+    myVcsSupport = new MercurialAgentSideVcsSupport(pluginConfig, new AgentHgPathProvider(agentConfig), myMirrorManager);
 
     myLogger = myContext.mock(BuildProgressLogger.class);
     myContext.checking(new Expectations() {{
       allowing(myLogger).message(with(any(String.class)));
       allowing(myLogger).warning(with(any(String.class)));
     }});
-
-    myR1Dir = copy(new File("mercurial-tests/testData/subrepos/r1"));
-    copy(new File("mercurial-tests/testData/subrepos/r2"));
-    copy(new File("mercurial-tests/testData/subrepos/r3"));
   }
 
   @AfterMethod
   public void tearDown() {
     myTempFiles.cleanup();
+    for (Process p : myProcesses) {
+      p.destroy();
+    }
   }
 
 
   public void subrepository_url_changed() throws Exception {
-    VcsRootImpl root = new VcsRootBuilder()
-            .withUrl(myR1Dir.getAbsolutePath())
-            .build();
+    File myR1Dir = copy(new File("mercurial-tests/testData/subrepos/r1"));
+    copy(new File("mercurial-tests/testData/subrepos/r2"));
+    copy(new File("mercurial-tests/testData/subrepos/r3"));
+    VcsRootImpl root = vcsRoot().withUrl(myR1Dir.getAbsolutePath()).build();
     doUpdate(root, "34017377d9c3");
     doUpdate(root, "d350e7209906");
   }
 
 
+  public void should_support_local_mirrors_for_subrepos() throws Exception {
+    File r1 = copy(new File("mercurial-tests/testData/subrepos/http_r1"));
+    File r2 = copy(new File("mercurial-tests/testData/subrepos/http_r2"));
+    File r3 = copy(new File("mercurial-tests/testData/subrepos/http_r3"));
+    Process server1 = serve(r1, 8001);
+    Process server2 = serve(r2, 8002);
+    Process server3 = serve(r3, 8003);
+    myProcesses.addAll(asList(server1, server2, server3));
+
+    final String url1 = "http://localhost:8001";
+    final String url2 = "http://localhost:8002";
+    final String url3 = "http://localhost:8003";
+    VcsRoot root1 = vcsRoot().withUrl(url1).build();
+    VcsRoot root3 = vcsRoot().withUrl(url3).build();
+
+    myWorkDir = new File(myDefaultWorkDir, "1");
+    doUpdate(root1, "3d6694af00e4", true);
+    server1.destroy();
+
+    HgRepo mirror1 = new HgRepo(myMirrorManager.getMirrorDir(url1), getHgPath(), new AuthSettings());
+    assertTrue("R1 mirror not updated to the latest revision", mirror1.containsRevision("3d6694af00e4"));
+
+    try {
+      myWorkDir = new File(myDefaultWorkDir, "3");
+      doUpdate(root3, "e1dc5eeb1bec", true);
+      doUpdate(root3, "999fb1eb9735", true);
+    } catch (ConnectionRefusedException e) {
+      fail("Contact remote repository when up-to-date subrepo local mirror exists");
+    }
+
+    HgRepo mirror2 = new HgRepo(myMirrorManager.getMirrorDir(url2), getHgPath(), new AuthSettings());
+    assertTrue("R2 mirror not updated to the latest revision", mirror2.containsRevision("7de2f844e1a2"));
+  }
+
+  private Process serve(@NotNull File repo, int port) throws IOException {
+    return new ProcessBuilder(getHgPath(), "serve", "-a", "localhost", "-p", String.valueOf(port)).directory(repo).start();
+  }
+
+
   private void doUpdate(final VcsRoot vcsRoot, final String toVersion) throws VcsException {
+    doUpdate(vcsRoot, toVersion, false);
+  }
+
+  private void doUpdate(final VcsRoot vcsRoot, final String toVersion, final boolean useLocalMirrors) throws VcsException {
     final AgentRunningBuild build = myContext.mock(AgentRunningBuild.class, "build" + myBuildCounter++);
     myContext.checking(new Expectations() {{
       allowing(build).getBuildLogger(); will(returnValue(myLogger));
-      allowing(build).getSharedConfigParameters(); will(returnValue(Collections.emptyMap()));
+      allowing(build).getSharedConfigParameters(); will(returnValue(new HashMap<String, String>() {{
+        put("teamcity.hg.use.local.mirrors", useLocalMirrors ? "true" : "false");
+      }}));
     }});
     myVcsSupport.getUpdater(vcsRoot, CheckoutRules.DEFAULT, toVersion, myWorkDir, build, false).process(IncludeRule.createDefaultInstance(), myWorkDir);
   }
@@ -103,4 +163,6 @@
     }
     return copyDir;
   }
+
+
 }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVersionTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVersionTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -1,6 +1,6 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial;
 
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.ParseHgVersionException;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.ParseHgVersionException;
 import junit.framework.TestCase;
 import org.jetbrains.annotations.NotNull;
 import org.testng.annotations.Test;
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -193,7 +193,7 @@
     moveDirWithContent(new File(r1, "hg"), new File(r1, ".hg"));
     moveDirWithContent(new File(r3, "hg"), new File(r3, ".hg"));
 
-    VcsRootImpl root = createVcsRoot(r1.getAbsolutePath());
+    VcsRootImpl root = vcsRoot().withUrl(r1.getAbsolutePath()).build();
 
     setName("clean_patch_with_subrepositories");
     checkPatchResult(buildPatch(root, null, "3:d350e7209906", CheckoutRules.DEFAULT).toByteArray());
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/UnrelatedResitoriesTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/UnrelatedResitoriesTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -1,8 +1,8 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial;
 
+import com.intellij.openapi.diagnostic.Logger;
 import jetbrains.buildServer.TempFiles;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.UnknownRevisionException;
+import jetbrains.buildServer.log.Log4jFactory;
 import jetbrains.buildServer.util.FileUtil;
 import jetbrains.buildServer.vcs.CheckoutRules;
 import jetbrains.buildServer.vcs.VcsException;
@@ -34,6 +34,10 @@
   private Mockery myContext;
   private ServerPluginConfig myPluginConfig;
 
+  static {
+    Logger.setFactory(new Log4jFactory());
+  }
+
   @BeforeMethod
   public void setUp() throws Exception {
     myContext = new Mockery();
@@ -64,14 +68,14 @@
 
 
   public void should_return_no_changes_when_fromRevision_is_from_unrelated_repository() throws Exception {
-    final CommandFactory factory = myContext.mock(CommandFactory.class);
-    final CollectChangesCommand commandExecutedWithException = myContext.mock(CollectChangesCommand.class);
-    myVcs = createVcs(factory);
+//    final CommandFactory factory = myContext.mock(CommandFactory.class);
+//    final CollectChangesCommand commandExecutedWithException = myContext.mock(CollectChangesCommand.class);
+    myVcs = createVcs();
     myContext.checking(new Expectations(){{
-      allowing(factory).getCollectChangesCommand(with(any(Settings.class)), with(any(File.class)));
-      will(returnValue(commandExecutedWithException));
-      allowing(commandExecutedWithException).execute(with(any(String.class)), with(any(String.class)));
-      will(throwException(new UnknownRevisionException("1234")));
+//      allowing(factory).getCollectChangesCommand(with(any(Settings.class)), with(any(File.class)));
+//      will(returnValue(commandExecutedWithException));
+//      allowing(commandExecutedWithException).call(with(any(String.class)), with(any(String.class)));
+//      will(throwException(new UnknownRevisionException("1234")));
     }});
 
     String currentVersionOfOldRepo = syncRepository();
@@ -98,7 +102,7 @@
     return Util.createMercurialServerSupport(myContext, myPluginConfig);
   }
 
-  private MercurialVcsSupport createVcs(@NotNull final CommandFactory factory) throws IOException {
-    return Util.createMercurialServerSupport(myContext, myPluginConfig, factory);
-  }
+//  private MercurialVcsSupport createVcs(/*@NotNull final CommandFactory factory*/) throws IOException {
+//    return Util.createMercurialServerSupport(myContext, myPluginConfig, new CommandFactoryImpl(myPluginConfig));
+//  }
 }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java	Fri Mar 02 14:24:58 2012 +0400
@@ -32,11 +32,11 @@
 
 
   public static MercurialVcsSupport createMercurialServerSupport(@NotNull Mockery context, ServerPluginConfig config) throws IOException {
-    return createMercurialServerSupport(context, config, new CommandFactoryImpl(config));
+    return createMercurialServerSupport(context, config, new RepoFactory(config));
   }
 
 
-  public static MercurialVcsSupport createMercurialServerSupport(@NotNull Mockery context, @NotNull ServerPluginConfig config, @NotNull CommandFactory commandFactory) throws IOException {
+  public static MercurialVcsSupport createMercurialServerSupport(@NotNull Mockery context, @NotNull ServerPluginConfig config, @NotNull RepoFactory commandFactory) throws IOException {
     VcsManager vcsManager = context.mock(VcsManager.class);
     final SBuildServer server = context.mock(SBuildServer.class);
     final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java	Fri Mar 02 14:24:58 2012 +0400
@@ -77,7 +77,7 @@
     settings.setCustomWorkingDir(workingDir);
     try {
       if (myCloneRequired) {
-        new CloneCommand(settings, workingDir).execute();
+        new HgRepo(workingDir, settings.getHgCommandPath(), settings.getAuthSettings()).doClone().fromRepository(settings.getRepository()).call();
       }
 
       return executor.execute(settings, workingDir);
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommandTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommandTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -1,5 +1,6 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
 
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnknownFileException;
 import jetbrains.buildServer.util.FileUtil;
 import jetbrains.buildServer.vcs.VcsException;
 import org.jetbrains.annotations.NotNull;
@@ -38,8 +39,7 @@
     setRepository("mercurial-tests/testData/rep1", true);
     runCommand(new CommandExecutor<File>() {
       public File execute(@NotNull final Settings settings, @NotNull final File workingDir) throws VcsException {
-        CatCommand cat = new CatCommand(settings, workingDir);
-        return cat.execute(asList("/non/existing/path"), false);
+        return new CatCommand(settings.getHgCommandPath(), workingDir, settings.getAuthSettings()).files("/non/existing/path").checkForFailure(false).call();
       }
     });
   }
@@ -66,7 +66,7 @@
   private File runCat(@NotNull final List<String> paths) throws IOException, VcsException {
     return runCommand(new CommandExecutor<File>() {
       public File execute(@NotNull final Settings settings, @NotNull final File workingDir) throws VcsException {
-        CatCommand cat = new CatCommand(settings, workingDir);
+        CatCommand cat = new CatCommand(settings.getHgCommandPath(), workingDir, settings.getAuthSettings());
         return cat.execute(paths);
       }
     });
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommandTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommandTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -46,8 +46,7 @@
     Settings settings = new Settings(new ServerHgPathProvider(config), root);
     settings.setCustomWorkingDir(workingDir);
 
-    CloneCommand clone = new CloneCommand(settings, workingDir);
-    clone.execute();
+    new CloneCommand(settings.getHgCommandPath(), workingDir, settings.getAuthSettings()).fromRepository(settings.getRepository()).call();
 
     String[] files = new String[] {".hg", "dir1", "dir with space", "file.txt"};
     for (String f : files) {
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -3,6 +3,8 @@
 import com.intellij.openapi.diagnostic.Logger;
 import jetbrains.buildServer.ExecResult;
 import jetbrains.buildServer.StreamGobbler;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnknownRevisionException;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnrelatedRepositoryException;
 import jetbrains.buildServer.vcs.VcsException;
 import org.apache.log4j.Level;
 import org.jetbrains.annotations.NonNls;
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommandTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommandTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -37,10 +37,10 @@
   private Void runIdentify(final ChangeSet cset) throws IOException, VcsException {
     return runCommand(new CommandExecutor<Void>() {
       public Void execute(@NotNull final Settings settings, File workingDir) throws VcsException {
-        IdentifyCommand identify = new IdentifyCommand(settings, workingDir);
-        identify.setInLocalRepository(true);
-        identify.setChangeSet(cset);
-        identify.execute();
+        new IdentifyCommand(settings.getHgCommandPath(), workingDir, settings.getAuthSettings())
+                .revision(cset)
+                .inLocalRepository()
+                .call();
         return null;
       }
     });
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -174,10 +174,12 @@
   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, myTemplateFile);
-        lc.setFromRevId(fromId);
-        lc.setToRevId(toId);
-        return lc.execute();
+        return new LogCommand(settings.getHgCommandPath(), workingDir, settings.getAuthSettings())
+                .inBranch(settings.getBranchName())
+                .withTemplate(myTemplateFile)
+                .fromRevision(fromId)
+                .toRevision(toId)
+                .call();
       }
     });
   }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommandTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommandTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -36,8 +36,7 @@
     try {
       runCommand(new CommandExecutor<Boolean>() {
         public Boolean execute(@NotNull final Settings settings, @NotNull File workingDir) throws VcsException {
-          PushCommand cmd = new PushCommand(settings, workingDir);
-          cmd.execute();
+          new PushCommand(settings.getHgCommandPath(), workingDir, settings.getAuthSettings()).toRepository(settings.getRepository()).call();
           return null;
         }
       });
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommandTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommandTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -55,10 +55,10 @@
   private List<ModifiedFile> runStatus(final String fromId, final String toId) throws IOException, VcsException {
     return runCommand(new CommandExecutor<List<ModifiedFile>>() {
       public List<ModifiedFile> execute(@NotNull final Settings settings, @NotNull final File workingDir) throws VcsException {
-        StatusCommand st = new StatusCommand(settings, workingDir);
-        st.setFromRevId(fromId);
-        st.setToRevId(toId);
-        return st.execute();
+        return new StatusCommand(settings.getHgCommandPath(), workingDir, settings.getAuthSettings())
+                .fromRevision(fromId)
+                .toRevision(toId)
+                .call();
       }
     });
   }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommandTest.java	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommandTest.java	Fri Mar 02 14:24:58 2012 +0400
@@ -18,7 +18,7 @@
     ServerPluginConfig config = new ServerPluginConfigBuilder().build();
     Settings settings = new Settings(new ServerHgPathProvider(config), root);
     VersionCommand versionCommand = new VersionCommand(settings, new File(".."));
-    HgVersion version = versionCommand.execute();
+    HgVersion version = versionCommand.call();
     assertNotNull(version);
   }
 
--- a/mercurial-tests/testData/subrepos/README	Thu Mar 01 20:15:42 2012 +0400
+++ b/mercurial-tests/testData/subrepos/README	Fri Mar 02 14:24:58 2012 +0400
@@ -8,4 +8,23 @@
 0:916933c1dd8e Initial commit
 
 r3 history:
-0:9e4a2fef1a1c Initial commit
\ No newline at end of file
+0:9e4a2fef1a1c Initial commit
+
+
+http subrepos:
+
+http_r1:
+2:3d6694af00e4 third 
+1:a2d737193093 second 
+0:2516e4a826e8 initial commit
+
+http_r2:
+2:7de2f844e1a2 Register subrepo r1 -> r1:1
+1:f54386da1b42 add .hgsub
+0:97bef7492664 initial
+
+http_r3:
+3:999fb1eb9735 Update subrepos -> r1:2, r2:2
+2:e1dc5eeb1bec register subrepos -> r1:1, r2:1
+1:94b99ee983ed add .hgsub
+0:44c53c0ea688 initial
Binary file mercurial-tests/testData/subrepos/http_r1/hg/00changelog.i has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r1/hg/branchheads.cache	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,2 @@
+a2d73719309307517fd15eccb8afefb61c3c9af7 1
+a2d73719309307517fd15eccb8afefb61c3c9af7 default
Binary file mercurial-tests/testData/subrepos/http_r1/hg/dirstate has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r1/hg/last-message.txt	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,1 @@
+third
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r1/hg/requires	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,4 @@
+revlogv1
+store
+fncache
+dotencode
Binary file mercurial-tests/testData/subrepos/http_r1/hg/store/00changelog.i has changed
Binary file mercurial-tests/testData/subrepos/http_r1/hg/store/00manifest.i has changed
Binary file mercurial-tests/testData/subrepos/http_r1/hg/store/data/a.i has changed
Binary file mercurial-tests/testData/subrepos/http_r1/hg/store/data/b.i has changed
Binary file mercurial-tests/testData/subrepos/http_r1/hg/store/data/c.i has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r1/hg/store/fncache	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,3 @@
+data/a.i
+data/b.i
+data/c.i
Binary file mercurial-tests/testData/subrepos/http_r1/hg/store/undo has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r1/hg/tags.cache	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,2 @@
+2 3d6694af00e4ee8d8ea7924c38b58c40f08609d4
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r1/hg/undo.branch	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,1 @@
+default
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r1/hg/undo.desc	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,2 @@
+2
+commit
Binary file mercurial-tests/testData/subrepos/http_r1/hg/undo.dirstate has changed
Binary file mercurial-tests/testData/subrepos/http_r2/hg/00changelog.i has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r2/hg/branchheads.cache	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,2 @@
+f54386da1b42a9f8b1773aa26074d178f9afe28f 1
+f54386da1b42a9f8b1773aa26074d178f9afe28f default
Binary file mercurial-tests/testData/subrepos/http_r2/hg/dirstate has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r2/hg/last-message.txt	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,1 @@
+Register subrepo r1
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r2/hg/requires	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,4 @@
+revlogv1
+store
+fncache
+dotencode
Binary file mercurial-tests/testData/subrepos/http_r2/hg/store/00changelog.i has changed
Binary file mercurial-tests/testData/subrepos/http_r2/hg/store/00manifest.i has changed
Binary file mercurial-tests/testData/subrepos/http_r2/hg/store/data/a.i has changed
Binary file mercurial-tests/testData/subrepos/http_r2/hg/store/data/~2ehgsub.i has changed
Binary file mercurial-tests/testData/subrepos/http_r2/hg/store/data/~2ehgsubstate.i has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r2/hg/store/fncache	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,3 @@
+data/a.i
+data/.hgsub.i
+data/.hgsubstate.i
Binary file mercurial-tests/testData/subrepos/http_r2/hg/store/undo has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r2/hg/tags.cache	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,2 @@
+2 7de2f844e1a209e9f3fd0395465862fdd1361709
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r2/hg/undo.branch	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,1 @@
+default
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r2/hg/undo.desc	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,2 @@
+2
+commit
Binary file mercurial-tests/testData/subrepos/http_r2/hg/undo.dirstate has changed
Binary file mercurial-tests/testData/subrepos/http_r3/hg/00changelog.i has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r3/hg/branchheads.cache	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,2 @@
+e1dc5eeb1bece84478e9c85aec0f784d43e4b310 2
+e1dc5eeb1bece84478e9c85aec0f784d43e4b310 default
Binary file mercurial-tests/testData/subrepos/http_r3/hg/dirstate has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r3/hg/last-message.txt	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,1 @@
+Update subrepos
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r3/hg/requires	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,4 @@
+revlogv1
+store
+fncache
+dotencode
Binary file mercurial-tests/testData/subrepos/http_r3/hg/store/00changelog.i has changed
Binary file mercurial-tests/testData/subrepos/http_r3/hg/store/00manifest.i has changed
Binary file mercurial-tests/testData/subrepos/http_r3/hg/store/data/a.i has changed
Binary file mercurial-tests/testData/subrepos/http_r3/hg/store/data/~2ehgsub.i has changed
Binary file mercurial-tests/testData/subrepos/http_r3/hg/store/data/~2ehgsubstate.i has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r3/hg/store/fncache	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,3 @@
+data/a.i
+data/.hgsub.i
+data/.hgsubstate.i
Binary file mercurial-tests/testData/subrepos/http_r3/hg/store/undo has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r3/hg/undo.branch	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,1 @@
+default
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/subrepos/http_r3/hg/undo.desc	Fri Mar 02 14:24:58 2012 +0400
@@ -0,0 +1,2 @@
+3
+commit
Binary file mercurial-tests/testData/subrepos/http_r3/hg/undo.dirstate has changed