changeset 883:39ff04730ccc

Report more vcs operation progress Instead of setting progress in each operation, create HgRepo via OpeationContext which adds progress to command settings.
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Sun, 05 Oct 2014 11:15:34 +0200
parents a421a669588c
children 31b9442acf2a
files mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BookmarksCommand.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/CloneCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandSettings.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MercurialCommandLine.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/TagsCommand.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesContext.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialCollectChangesPolicy.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialFetchService.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialModificationInfoBuilder.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/OperationContext.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/RepoFactory.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesCommand.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesWithRevsets.java
diffstat 19 files changed, 206 insertions(+), 173 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java	Sun Oct 05 11:15:34 2014 +0200
@@ -21,6 +21,7 @@
 import java.io.File;
 import java.util.Set;
 
+import static com.intellij.openapi.util.text.StringUtil.isEmpty;
 import static java.util.Collections.emptySet;
 
 /**
@@ -56,6 +57,7 @@
     final MercurialCommandLine cl = new MercurialCommandLine(getPrivateData());
     cl.setExePath(myHgPath);
     cl.setEnvParams(myCommandSettings.getHgEnv());
+    cl.setDescription(getDescription());
 
     //include global arguments if any
     cl.addParameters(myCommandSettings.getGlobalArguments());
@@ -73,7 +75,15 @@
                                            @NotNull final CommandSettings commandSettings) throws VcsException {
 
     if (!myCommandSettings.getUseCommandlineViaFileWrapper()) {
-      return CommandUtil.runCommand(cli, commandSettings.setPrivateData(getPrivateData()));
+      String commandDescription = cli.getDescription();
+      if (!isEmpty(commandDescription))
+        commandSettings.getProgress().reportProgress(commandDescription);
+      try {
+        return CommandUtil.runCommand(cli, commandSettings.setPrivateData(getPrivateData()));
+      } finally {
+        if (!isEmpty(commandDescription))
+          commandSettings.getProgress().reportProgress(commandDescription + " finished");
+      }
     }
 
     return CommandUtil.runWrappedCommand(cli, commandSettings);
@@ -93,4 +103,9 @@
   protected String getHgPath() {
     return myHgPath;
   }
+
+  @NotNull
+  protected String getDescription() {
+    return "";
+  }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BookmarksCommand.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BookmarksCommand.java	Sun Oct 05 11:15:34 2014 +0200
@@ -38,4 +38,10 @@
   protected String getBranchesCommand() {
     return "bookmarks";
   }
+
+  @NotNull
+  @Override
+  protected String getDescription() {
+    return "hg bookmarks";
+  }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BranchesCommand.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BranchesCommand.java	Sun Oct 05 11:15:34 2014 +0200
@@ -64,4 +64,10 @@
   protected String getBranchesCommand() {
     return "branches";
   }
+
+  @NotNull
+  @Override
+  protected String getDescription() {
+    return "hg branches";
+  }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java	Sun Oct 05 11:15:34 2014 +0200
@@ -112,17 +112,31 @@
       MercurialCommandLine cli = createCommandLine(tempDir);
       int cmdSize = cli.getCommandLineLength() + 42;
 
+      List<String> pathsForDescription = new ArrayList<String>(2);
       do {
         String path = paths.poll();
+        if (pathsForDescription.size() < 2)
+          pathsForDescription.add(path);
         cli.addParameter(path);
         cmdSize += path.length() + 3; //quotes + space
       } while (cmdSize < myCommandSettings.getMaxCommandLineSize() && !paths.isEmpty());
 
+      cli.setDescription(getDescription(pathsForDescription));
       runCommand(cli, myCommandSettings.setCheckForFailure(checkFailure));
     }
   }
 
   @NotNull
+  private String getDescription(List<String> pathsForDescription) {
+    StringBuilder description = new StringBuilder();
+    description.append("hg cat -r ").append(myRevId).append(" ");
+    for (String p : pathsForDescription) {
+      description.append(p).append(" ");
+    }
+    return description.toString();
+  }
+
+  @NotNull
   private MercurialCommandLine createCommandLine(@NotNull final File tempDir) {
     final MercurialCommandLine cli = createCommandLine();
     addHttpAuthParams(cli);
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java	Sun Oct 05 11:15:34 2014 +0200
@@ -29,7 +29,6 @@
   private boolean myUsePullProtocol = true;
   private boolean myUseUncompressedTransfer = false;
   private boolean myTraceback;
-  private ProgressParser.ProgressConsumer myProgressConsumer;
 
   public CloneCommand(@NotNull CommandSettings commandSettings,
                       @NotNull String hgPath,
@@ -77,11 +76,6 @@
     return this;
   }
 
-  public CloneCommand withProgressConsumer(ProgressParser.ProgressConsumer progressConsumer) {
-    myProgressConsumer = progressConsumer;
-    return this;
-  }
-
   public void call() throws VcsException {
     myWorkingDir.mkdirs();
     MercurialCommandLine cli = createCommandLine();
@@ -104,12 +98,11 @@
     }
 
     CommandSettings settings = myCommandSettings.setTimeout(24 * 3600); // some repositories are quite large, we set timeout to 24 hours
-    if (myProgressConsumer != null) {
+    if (settings.getProgressConsumer() != null) {
       cli.addParameters("--config", "extensions.progress=");
       cli.addParameters("--config", "progress.format=topic number");
       cli.addParameters("--config", "progress.delay=0");
       cli.addParameters("--config", "progress.assume-tty=True");
-      settings.setProgressConsumer(myProgressConsumer);
     }
 
     String repositoryUrl = myAuthSettings.getRepositoryUrlWithCredentials(myRepository);
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandSettings.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandSettings.java	Sun Oct 05 11:15:34 2014 +0200
@@ -16,8 +16,10 @@
 
 package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
 
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialProgress;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.OS;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.*;
 
@@ -35,8 +37,8 @@
   private int myLogOutputLimit = -1;
   private int myExceptionOutputLimit = 5000;
   private List<String> myGlobalArguments = new ArrayList<String>(0);
-  private ProgressParser.ProgressConsumer myProgressConsumer;
   private boolean myUseCommandlineViaFileWrapper = false;
+  private MercurialProgress myProgress = MercurialProgress.NO_OP;
 
   public final int getMaxCommandLineSize() {
     return myUseCommandlineViaFileWrapper ? Integer.MAX_VALUE : OS.getMaxCommandLineSize();
@@ -135,11 +137,23 @@
     myExceptionOutputLimit = limit;
   }
 
+  @Nullable
   public ProgressParser.ProgressConsumer getProgressConsumer() {
-    return myProgressConsumer;
+    if (myProgress == MercurialProgress.NO_OP)
+      return null;
+    return new ProgressParser.ProgressConsumer() {
+      public void consume(float progress, @NotNull String stage) {
+        myProgress.reportProgress(progress, stage);
+      }
+    };
   }
 
-  public void setProgressConsumer(ProgressParser.ProgressConsumer progressConsumer) {
-    myProgressConsumer = progressConsumer;
+  @NotNull
+  public MercurialProgress getProgress() {
+    return myProgress;
+  }
+
+  public void setProgress(@NotNull MercurialProgress progress) {
+    myProgress = progress;
   }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Sun Oct 05 11:15:34 2014 +0200
@@ -209,4 +209,19 @@
             .inLocalRepository()
             .call();
   }
+
+  @NotNull
+  @Override
+  protected String getDescription() {
+    StringBuilder result = new StringBuilder();
+    result.append("hg log ");
+    if (myRevsets != null) {
+      result.append(myRevsets);
+    } else {
+      String from = myFromId != null ? myFromId : "0";
+      String to = myToId != null ? myToId : "tip";
+      result.append(from).append(":").append(to);
+    }
+    return result.toString();
+  }
 }
\ No newline at end of file
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MercurialCommandLine.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MercurialCommandLine.java	Sun Oct 05 11:15:34 2014 +0200
@@ -32,6 +32,7 @@
   private String myWorkingDirectory;
   private final Set<String> myPrivateData;
   private Charset myCharset;
+  private String myDescription = "";
 
   public MercurialCommandLine(@NotNull final Set<String> privateData) {
     myPrivateData = privateData;
@@ -160,4 +161,13 @@
 
     return cmd;
   }
+
+  @NotNull
+  public String getDescription() {
+    return myDescription;
+  }
+
+  public void setDescription(@NotNull String description) {
+    myDescription = description;
+  }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java	Sun Oct 05 11:15:34 2014 +0200
@@ -33,7 +33,6 @@
   private int myTimeout;
   private boolean myTraceback;
   private boolean myProfile;
-  private ProgressParser.ProgressConsumer myProgressConsumer;
 
   public PullCommand(@NotNull CommandSettings commandSettings,
                      @NotNull String hgPath,
@@ -67,11 +66,6 @@
     return this;
   }
 
-  public PullCommand withProgressConsumer(ProgressParser.ProgressConsumer progressConsumer) {
-    myProgressConsumer = progressConsumer;
-    return this;
-  }
-
   public void call() throws VcsException {
     ensureRepositoryIsNotLocked();
     MercurialCommandLine cli = createCommandLine();
@@ -83,23 +77,16 @@
       cli.addParameter("--profile");
 
     CommandSettings settings = myCommandSettings.setTimeout(myTimeout);
-    if (myProgressConsumer != null) {
+    if (settings.getProgressConsumer() != null) {
       cli.addParameters("--config", "extensions.progress=");
       cli.addParameters("--config", "progress.format=topic number");
       cli.addParameters("--config", "progress.delay=0");
       cli.addParameters("--config", "progress.assume-tty=True");
-      myProgressConsumer.consume(-1f, "hg pull " + myAuthSettings.getRepositoryUrlWithHiddenPassword(myPullUrl));
-      settings.setProgressConsumer(myProgressConsumer);
     }
 
     String pullUrl = myAuthSettings.getRepositoryUrlWithCredentials(myPullUrl);
     cli.addParameter(pullUrl);
-    try {
-      runCommand(cli, settings);
-    } finally {
-      if (myProgressConsumer != null)
-        myProgressConsumer.consume(-1f, "hg pull " + myAuthSettings.getRepositoryUrlWithHiddenPassword(myPullUrl) + " finished");
-    }
+    runCommand(cli, settings);
   }
 
   private void ensureRepositoryIsNotLocked() {
@@ -112,4 +99,10 @@
   private File getRepositoryLock() {
     return new File(getWorkDirectory(), ".hg" + File.separator + "store" + File.separator + "lock");
   }
+
+  @NotNull
+  @Override
+  protected String getDescription() {
+    return "hg pull " + myAuthSettings.getRepositoryUrlWithHiddenPassword(myPullUrl);
+  }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/TagsCommand.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/TagsCommand.java	Sun Oct 05 11:15:34 2014 +0200
@@ -43,4 +43,10 @@
     raw.remove("tip");
     return raw;
   }
+
+  @NotNull
+  @Override
+  protected String getDescription() {
+    return "hg tags";
+  }
 }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesContext.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CollectChangesContext.java	Sun Oct 05 11:15:34 2014 +0200
@@ -19,7 +19,6 @@
 import com.intellij.openapi.util.Pair;
 import gnu.trove.TLongObjectHashMap;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.ProgressParser;
 import jetbrains.buildServer.util.Hash;
 import jetbrains.buildServer.util.graph.BFSVisitorAdapter;
 import jetbrains.buildServer.util.graph.DAG;
@@ -39,7 +38,6 @@
 public class CollectChangesContext extends OperationContext {
 
   private final Set<String> myUninterestingRevisions;
-  private final RepositoryStateData myToState;
   private final Map<VcsRootKey, DAG<String>> myDags = new HashMap<VcsRootKey, DAG<String>>();
   private Set<File> mySyncedDirs = new HashSet<File>();
   private Map<String, Set<SubrepoChangesInterval>> myProcessedSubrepoChanges = new HashMap<String, Set<SubrepoChangesInterval>>();//subrepo url -> processed changes intervals
@@ -47,41 +45,34 @@
   private Map<File, ServerHgRepo> myRepos = new HashMap<File, ServerHgRepo>();
   private boolean myIncludeFromRevisions = false;//by default don't include them, they should be included only for subrepos
   private TLongObjectHashMap<String> myStringPool = new TLongObjectHashMap<String>();
-  private ProgressParser.ProgressConsumer myProgressConsumer;
 
   public CollectChangesContext(@NotNull MercurialVcsSupport vcs,
                                @NotNull RepoFactory repoFactory,
                                @NotNull MercurialProgress progress,
-                               @NotNull RepositoryStateData fromState,
-                               @NotNull RepositoryStateData toState) {
-    this(vcs, repoFactory, progress, new HashSet<String>(fromState.getBranchRevisions().values()), toState);
+                               @NotNull RepositoryStateData fromState) {
+    this(vcs, repoFactory, progress, new HashSet<String>(fromState.getBranchRevisions().values()));
   }
 
   public CollectChangesContext(@NotNull MercurialVcsSupport vcs,
                                @NotNull RepoFactory repoFactory,
                                @NotNull MercurialProgress progress,
-                               @NotNull String fromVersion,
-                               @NotNull String toVersion) {
-    this(vcs, repoFactory, progress, setOf(fromVersion), RepositoryStateData.createSingleVersionState(toVersion));
+                               @NotNull String fromVersion) {
+    this(vcs, repoFactory, progress, setOf(fromVersion));
   }
 
   public CollectChangesContext(@NotNull MercurialVcsSupport vcs,
                                @NotNull RepoFactory repoFactory,
                                @NotNull MercurialProgress progress,
-                               @NotNull Collection<String> fromVersions,
-                               @NotNull RepositoryStateData toState) {
+                               @NotNull Collection<String> fromVersions) {
     super(vcs, repoFactory, progress);
-    myToState = toState;
     myUninterestingRevisions = new HashSet<String>(fromVersions);
   }
 
-  public void syncRepository(@NotNull HgVcsRoot root, @Nullable ProgressParser.ProgressConsumer progressConsumer) throws VcsException {
+  public void syncRepository(@NotNull HgVcsRoot root) throws VcsException {
     File dir = myVcs.getWorkingDir(root);
     if (mySyncedDirs.contains(dir))
       return;
-    SyncSettings<Void> settings = new SyncSettings<Void>(VcsCallable.NO_OP);
-    settings.setProgressConsumer(progressConsumer);
-    myVcs.syncRepository(root, settings);
+    super.syncRepository(root);
     mySyncedDirs.add(dir);
   }
 
@@ -116,6 +107,13 @@
     return repo;
   }
 
+  @NotNull
+  @Override
+  public ServerHgRepo createRepo(@NotNull HgVcsRoot root) throws VcsException {
+    File workingDir = myVcs.getWorkingDir(root);
+    return createRepo(root, workingDir);
+  }
+
   public boolean isReportedModification(@NotNull ModificationData m) {
     Set<String> revisions = getReportedRootRevisions(m);
     return revisions.contains(m.getVersion());
@@ -196,15 +194,15 @@
   public Collection<String> getFromRevisionsForBranch(@NotNull HgVcsRoot root,
                                                       @NotNull String fromRevision,
                                                       @NotNull String toRevision,
-                                                      @Nullable ProgressParser.ProgressConsumer progressConsumer) throws VcsException {
-    syncRepository(root, progressConsumer);
+                                                      @NotNull RepositoryStateData toState) throws VcsException {
+    syncRepository(root);
     ServerHgRepo repo = createRepo(root, myVcs.getWorkingDir(root));
 
     if (!repo.supportRevsets())
       return singleton(fromRevision);
 
     Set<String> fromRevisions = new HashSet<String>();
-    if (myToState.getBranchRevisions().size() > 1) {
+    if (toState.getBranchRevisions().size() > 1) {
       VcsRootKey rootKey = VcsRootKey.create(root);
       DAG<String> dag = myDags.get(rootKey);
       if (dag == null) {
@@ -304,13 +302,4 @@
       }
     }
   }
-
-  @Nullable
-  public ProgressParser.ProgressConsumer getProgressConsumer() {
-    return myProgressConsumer;
-  }
-
-  public void setProgressConsumer(ProgressParser.ProgressConsumer progressConsumer) {
-    myProgressConsumer = progressConsumer;
-  }
 }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialCollectChangesPolicy.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialCollectChangesPolicy.java	Sun Oct 05 11:15:34 2014 +0200
@@ -56,15 +56,14 @@
 
   @NotNull
   public RepositoryStateData getCurrentState(@NotNull VcsRoot root) throws VcsException {
+    final OperationContext context = new OperationContext(myVcs, myRepoFactory, createMercurialProgess());
     final HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root);
     VcsCallable<Map<String, String>> cmd = new VcsCallable<Map<String, String>>() {
       public Map<String, String> call() throws VcsException {
-        return getHeads(hgRoot);
+        return getHeads(hgRoot, context);
       }
     };
-    SyncSettings<Map<String, String>> settings = new SyncSettings<Map<String, String>>(cmd);
-    settings.setProgressConsumer(createProgressConsumer());
-    Map<String, String> revisions = myVcs.syncRepository(hgRoot, settings);
+    Map<String, String> revisions = context.syncRepository(hgRoot, cmd);
     String defaultBranchName = hgRoot.getBranchName();
     if (revisions.get(defaultBranchName) == null && !hgRoot.isIgnoreMissingDefaultBranch()) {
       throw new VcsException("Cannot find revision of the default branch '" +
@@ -74,12 +73,18 @@
   }
 
   @NotNull
-  public Map<String, String> getHeads(@NotNull final HgVcsRoot hgRoot) throws VcsException {
+  public Map<String, String> getHeads(@NotNull HgVcsRoot hgRoot) throws VcsException {
     boolean includeTags = myConfig.useTagsAsBranches() && hgRoot.useTagsAsBranches();
     return myVcs.createRepo(hgRoot).getBranchRevisions(myConfig.bookmarksEnabled(), includeTags);
   }
 
   @NotNull
+  public Map<String, String> getHeads(@NotNull HgVcsRoot hgRoot, @NotNull OperationContext context) throws VcsException {
+    boolean includeTags = myConfig.useTagsAsBranches() && hgRoot.useTagsAsBranches();
+    return context.createRepo(hgRoot).getBranchRevisions(myConfig.bookmarksEnabled(), includeTags);
+  }
+
+  @NotNull
   public List<ModificationData> collectChanges(@NotNull VcsRoot fromRoot,
                                                @NotNull RepositoryStateData fromState,
                                                @NotNull VcsRoot toRoot,
@@ -96,8 +101,7 @@
                                                @NotNull CheckoutRules rules) throws VcsException {
     List<ModificationData> changes = new ArrayList<ModificationData>();
     HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root);
-    CollectChangesContext ctx = new CollectChangesContext(myVcs, myRepoFactory, createMercurialProgess(), fromState, toState);
-    ctx.setProgressConsumer(createProgressConsumer());
+    CollectChangesContext ctx = new CollectChangesContext(myVcs, myRepoFactory, createMercurialProgess(), fromState);
     for (Map.Entry<String, String> entry : toState.getBranchRevisions().entrySet()) {
       String branch = entry.getKey();
       String toRevision = entry.getValue();
@@ -107,7 +111,7 @@
       if (toRevision.equals(fromRevision) || fromRevision == null)
         continue;
 
-      Collection<String> fromRevisions = ctx.getFromRevisionsForBranch(hgRoot, fromRevision, toRevision, ctx.getProgressConsumer());
+      Collection<String> fromRevisions = ctx.getFromRevisionsForBranch(hgRoot, fromRevision, toRevision, toState);
       List<ModificationData> branchChanges = collectChanges(ctx, root, fromRevisions, toRevision, rules);
       for (ModificationData change : branchChanges) {
         if (!ctx.isReportedModification(change)) {
@@ -128,9 +132,8 @@
                                                @Nullable String toRootRevision,
                                                @NotNull CheckoutRules checkoutRules) throws VcsException {
     HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(toRoot);
-    SyncSettings<Void> settings = new SyncSettings<Void>(VcsCallable.NO_OP);
-    settings.setProgressConsumer(createProgressConsumer());
-    myVcs.syncRepository(hgRoot, settings);
+    CollectChangesContext context = new CollectChangesContext(myVcs, myRepoFactory, createMercurialProgess(), fromRootRevision);
+    context.syncRepository(hgRoot);
     String toRevision = toRootRevision;
     if (toRevision == null) {
       RepositoryStateData state = myVcs.getCollectChangesPolicy().getCurrentState(toRoot);
@@ -139,7 +142,7 @@
     String mergeBase = getMergeBase(hgRoot, fromRootRevision, toRevision);
     if (mergeBase == null)
       return Collections.emptyList();
-    return collectChanges(toRoot, mergeBase, toRootRevision, checkoutRules);
+    return collectChanges(context, toRoot, mergeBase, toRootRevision, checkoutRules);
   }
 
 
@@ -149,14 +152,21 @@
                                                @NotNull CheckoutRules checkoutRules) throws VcsException {
     if (currentVersion == null)
       return emptyList();
-    CollectChangesContext ctx = new CollectChangesContext(myVcs, myRepoFactory, createMercurialProgess(), fromVersion, currentVersion);
-    ctx.setProgressConsumer(createProgressConsumer());
+    CollectChangesContext ctx = new CollectChangesContext(myVcs, myRepoFactory, createMercurialProgess(), fromVersion);
+    return collectChanges(ctx, root, fromVersion, currentVersion, checkoutRules);
+  }
+
+
+  private List<ModificationData> collectChanges(@NotNull CollectChangesContext ctx,
+                                                @NotNull VcsRoot root,
+                                                @NotNull String fromVersion,
+                                                @Nullable String currentVersion,
+                                                @NotNull CheckoutRules checkoutRules) throws VcsException {
     List<ModificationData> changes = collectChanges(ctx, root, asList(fromVersion), currentVersion, checkoutRules);
     changes.addAll(getSubrepoChanges(ctx, root, changes));
     return changes;
   }
 
-
   @Nullable
   private String getMergeBase(@NotNull HgVcsRoot root, @NotNull String revision1, @NotNull String revision2) throws VcsException {
     String result = myVcs.createRepo(root).mergeBase()
@@ -190,7 +200,7 @@
                                                 @Nullable String currentVersion,
                                                 @NotNull CheckoutRules checkoutRules) throws VcsException {
     HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root);
-    ctx.syncRepository(hgRoot, ctx.getProgressConsumer());
+    ctx.syncRepository(hgRoot);
     List<ModificationData> result = new ArrayList<ModificationData>();
     List<ChangeSet> csets = getChangesets(ctx, hgRoot, fromVersion, currentVersion);
     //When commit has no changes in subrepo configuration we can reuse
@@ -212,17 +222,16 @@
     if (toVersion == null)
       return emptyList();
     List<String> fromCommits = new ArrayList<String>();
-    for (String fromVersion : fromVersions) {
-      fromCommits.add(new ChangeSetRevision(fromVersion).getId());
+      for (String fromVersion : fromVersions) {
+        fromCommits.add(new ChangeSetRevision(fromVersion).getId());
     }
     String toCommit = new ChangeSetRevision(toVersion).getId();
     try {
-      ServerHgRepo repo = myVcs.createRepo(ctx, root);
+      ServerHgRepo repo = ctx.createRepo(root);
       List<ChangeSet> csets = repo.collectChanges(root)
               .fromRevision(fromCommits)
               .toRevision(toCommit)
               .includeFromRevision(ctx.includeFromRevisions())
-              .withProgressConsumer(ctx.getProgressConsumer())
               .call();
       if (!ctx.includeFromRevisions()) {
         Iterator<ChangeSet> iter = csets.iterator();
@@ -326,7 +335,7 @@
     List<HgSubrepoConfigChange> configChanges = new ArrayList<HgSubrepoConfigChange>();
 
     HgVcsRoot mainRoot = myHgVcsRootFactory.createHgRoot(m.getVcsRoot());
-    ServerHgRepo repo = myVcs.createRepo(ctx, mainRoot);
+    ServerHgRepo repo = ctx.createRepo(mainRoot);
     for (HgSubrepoConfigChange c : repo.getSubrepoConfigChanges(m)) {
       if (!(c.subrepoUrlChanged() || c.subrepoAdded() || c.subrepoRemoved())) {//report only changes in revisions, because we collect changes only for such changes
         //map url and path, relative to the main repository
@@ -359,7 +368,7 @@
     Map<String, String> attributes = new HashMap<String, String>();
     if (detectSubrepoChanges(mainHgRoot)) {
       try {
-        ServerHgRepo repo = myVcs.createRepo(ctx, mainHgRoot);
+        ServerHgRepo repo = ctx.createRepo(mainHgRoot);
         SubrepoRevisionAttributesBuilder attrBuilder = new SubrepoRevisionAttributesBuilder();
         for (SubRepo s : repo.getSubrepositories(m).values()) {
           attrBuilder.addSubrepo(new SubrepoConfig(mainRoot)
@@ -404,50 +413,4 @@
       return MercurialProgress.NO_OP;
     }
   }
-
-  @Nullable
-  private ProgressParser.ProgressConsumer createProgressConsumer() {
-    try {
-      final VcsOperationProgress progress = myProgressProvider.getProgress();
-      return new VcsOperationProgressConsumer(progress);
-    } catch (IllegalStateException e) {
-      return null;
-    }
-  }
-
-  private static class VcsOperationProgressConsumer implements ProgressParser.ProgressConsumer {
-    private final VcsOperationProgress myProgress;
-    private String myPrevMessage;
-    private int myPrevPercents;
-    public VcsOperationProgressConsumer(@NotNull VcsOperationProgress progress) {
-      myProgress = progress;
-    }
-
-    public void consume(float progressPercents, @NotNull String stage) {
-      if (progressPercents < 0) {
-        resetPrevProgress();
-        myProgress.reportProgress(stage);
-      } else {
-        int percents = (int) Math.floor(progressPercents * 100);
-        if (!isDuplicate(stage, percents)) {
-          myProgress.reportProgress(stage + " " + percents + "%");
-          updatePrevProgress(stage, percents);
-        }
-      }
-    }
-
-    private void resetPrevProgress() {
-      myPrevMessage = null;
-      myPrevPercents = -1;
-    }
-
-    private boolean isDuplicate(@NotNull String message, int percents) {
-      return message.equals(myPrevMessage) && percents == myPrevPercents;
-    }
-
-    private void updatePrevProgress(@NotNull String message, int percents) {
-      myPrevMessage = message;
-      myPrevPercents = percents;
-    }
-  }
 }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialFetchService.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialFetchService.java	Sun Oct 05 11:15:34 2014 +0200
@@ -16,7 +16,6 @@
 
 package jetbrains.buildServer.buildTriggers.vcs.mercurial;
 
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.ProgressParser;
 import jetbrains.buildServer.vcs.CheckoutRules;
 import jetbrains.buildServer.vcs.FetchService;
 import jetbrains.buildServer.vcs.VcsException;
@@ -27,30 +26,21 @@
 
   private final MercurialVcsSupport myVcs;
   private final HgVcsRootFactory myHgVcsRootFactory;
+  private final RepoFactory myRepoFactory;
 
   public MercurialFetchService(@NotNull MercurialVcsSupport vcs,
-                               @NotNull HgVcsRootFactory hgVcsRootFactory) {
+                               @NotNull HgVcsRootFactory hgVcsRootFactory,
+                               @NotNull RepoFactory repoFactory) {
     myVcs = vcs;
     myHgVcsRootFactory = hgVcsRootFactory;
+    myRepoFactory = repoFactory;
     vcs.addExtension(this);
   }
 
   public void fetchRepository(@NotNull VcsRoot root,
                               @NotNull CheckoutRules rules,
                               @NotNull FetchRepositoryCallback callback) throws VcsException {
-    SyncSettings<Void> settings = new SyncSettings<Void>(VcsCallable.NO_OP);
-    settings.setProgressConsumer(new FetchProgressConsumer(callback));
-    myVcs.syncRepository(myHgVcsRootFactory.createHgRoot(root));
-  }
-
-  private class FetchProgressConsumer implements ProgressParser.ProgressConsumer {
-    private final FetchRepositoryCallback myCallback;
-    private FetchProgressConsumer(@NotNull FetchRepositoryCallback callback) {
-      myCallback = callback;
-    }
-
-    public void consume(float progress, @NotNull String stage) {
-      myCallback.update(progress, stage);
-    }
+    OperationContext ctx = new OperationContext(myVcs, myRepoFactory, new MercurialFetchCallbackProgress(callback));
+    ctx.syncRepository(myHgVcsRootFactory.createHgRoot(root));
   }
 }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialModificationInfoBuilder.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialModificationInfoBuilder.java	Sun Oct 05 11:15:34 2014 +0200
@@ -51,8 +51,7 @@
     final CollectChangesContext ctx = new CollectChangesContext(myVcs,
             myRepoFactory,
             MercurialProgress.NO_OP,
-            Collections.<String>emptyList(),
-            RepositoryStateData.createVersionState("", Collections.<String, String>emptyMap()));
+            Collections.<String>emptyList());
 
     //TODO: it's better if we call log command once (or by chunks) instead of simple for-each
     for (String commitId : revisions) {
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Sun Oct 05 11:15:34 2014 +0200
@@ -553,13 +553,17 @@
   }
 
   public <T> T syncRepository(@NotNull HgVcsRoot root, @NotNull SyncSettings<T> settings) throws VcsException {
+    return syncRepository(root, settings, null);
+  }
+
+  public <T> T syncRepository(@NotNull HgVcsRoot root, @NotNull SyncSettings<T> settings, @Nullable OperationContext context) throws VcsException {
     boolean customWorkingDir = root.getCustomWorkingDir() != null;
     File workingDir = getWorkingDir(root);
     int attemptsLeft = 3;
     VcsException lastError = null;
     while (attemptsLeft-- > 0) {
       try {
-        return syncRepositoryOnce(root, settings, workingDir);
+        return syncRepositoryOnce(root, settings, workingDir, context);
       } catch (UnrelatedRepositoryException e) {
         if (customWorkingDir)
           throw new VcsException(e.getMessage() + ". VCS root uses a custom clone dir, manual recovery is required.", e);
@@ -580,9 +584,9 @@
   }
 
 
-  private <T> T syncRepositoryOnce(@NotNull HgVcsRoot root, @NotNull SyncSettings<T> settings, @NotNull File workingDir) throws VcsException {
+  private <T> T syncRepositoryOnce(@NotNull HgVcsRoot root, @NotNull SyncSettings<T> settings, @NotNull File workingDir, @Nullable OperationContext context) throws VcsException {
     lockWorkDir(workingDir, settings.getProgressConsumer());
-    HgRepo repo = createRepo(root);
+    HgRepo repo = context != null ? context.createRepo(root) : createRepo(root);
     try {
       if (!repo.isValidRepository())
         repo.init().call();
@@ -591,7 +595,6 @@
       resetBookmarks(repo);
       repo.pull().fromRepository(root.getRepository())
               .withTimeout(myConfig.getPullTimeout())
-              .withProgressConsumer(settings.getProgressConsumer())
               .withProfile(myConfig.runWithProfile(root))
               .call();
       return settings.getCmd().call();
@@ -779,11 +782,6 @@
   }
 
   @NotNull
-  public ServerHgRepo createRepo(@NotNull CollectChangesContext ctx, @NotNull HgVcsRoot root) throws VcsException {
-    return ctx.createRepo(root, getWorkingDir(root));
-  }
-
-  @NotNull
   public ServerHgRepo createRepo(@NotNull HgVcsRoot root, @NotNull File customDir) throws VcsException {
     return myRepoFactory.createRepo(root, customDir);
   }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/OperationContext.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/OperationContext.java	Sun Oct 05 11:15:34 2014 +0200
@@ -54,6 +54,22 @@
 
   @NotNull
   public ServerHgRepo createRepo(@NotNull HgVcsRoot root, @NotNull File workingDir) throws VcsException {
-    return myRepoFactory.createRepo(root, workingDir);
+    return myRepoFactory.createRepo(root, workingDir, myProgress);
+  }
+
+
+  public void syncRepository(@NotNull HgVcsRoot root) throws VcsException {
+    syncRepository(root, VcsCallable.NO_OP);
+  }
+
+  public <T> T syncRepository(@NotNull HgVcsRoot root, @NotNull VcsCallable<T> cmd) throws VcsException {
+    SyncSettings<T> settings = new SyncSettings<T>(cmd);
+    return myVcs.syncRepository(root, settings, this);
+  }
+
+
+  @NotNull
+  public ServerHgRepo createRepo(@NotNull HgVcsRoot root) throws VcsException {
+    return myRepoFactory.createRepo(root, myVcs.getWorkingDir(root), myProgress);
   }
 }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/RepoFactory.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/RepoFactory.java	Sun Oct 05 11:15:34 2014 +0200
@@ -16,10 +16,7 @@
 
 package jetbrains.buildServer.buildTriggers.vcs.mercurial;
 
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.AuthSettings;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandSettingsFactory;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandSettingsForRoot;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
 import jetbrains.buildServer.vcs.VcsException;
 import org.jetbrains.annotations.NotNull;
 
@@ -58,7 +55,29 @@
     return create(workingDir, myHgPathProvider.getHgPath(root), root.getAuthSettings(), myCommandSettingsFactory.forRoot(root), myConfig);
   }
 
+  @NotNull
+  public ServerHgRepo createRepo(@NotNull HgVcsRoot root, @NotNull File workingDir, @NotNull MercurialProgress progress) throws VcsException {
+    CommandSettingsFactory settingsFactory = myCommandSettingsFactory.forRoot(root);
+    return create(workingDir, myHgPathProvider.getHgPath(root), root.getAuthSettings(), new FactoryWithProgess(settingsFactory, progress), myConfig);
+  }
+
   public void dispose() {
   }
 
+  private static class FactoryWithProgess implements CommandSettingsFactory {
+    private final CommandSettingsFactory myDelegate;
+    private final MercurialProgress myProgress;
+    public FactoryWithProgess(@NotNull CommandSettingsFactory delegate, @NotNull MercurialProgress progress) {
+      myDelegate = delegate;
+      myProgress = progress;
+    }
+
+    @NotNull
+    public CommandSettings create() {
+      CommandSettings commandSettings = myDelegate.create();
+      commandSettings.setProgress(myProgress);
+      return commandSettings;
+    }
+  }
+
 }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesCommand.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesCommand.java	Sun Oct 05 11:15:34 2014 +0200
@@ -29,7 +29,6 @@
   protected List<String> myFromRevisions;
   protected String myToRevision;
   protected boolean myIncludeFromRevision;
-  protected ProgressParser.ProgressConsumer myProgressConsumer;
 
   @NotNull
   public abstract List<ChangeSet> call(@NotNull List<String> fromCommits, @NotNull String toCommit) throws VcsException;
@@ -46,11 +45,6 @@
     return this;
   }
 
-  public CollectChangesCommand withProgressConsumer(ProgressParser.ProgressConsumer progressConsumer) {
-    myProgressConsumer = progressConsumer;
-    return this;
-  }
-
   public CollectChangesCommand includeFromRevision(boolean doInclude) {
     myIncludeFromRevision = doInclude;
     return this;
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesWithRevsets.java	Sun Oct 05 09:32:01 2014 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesWithRevsets.java	Sun Oct 05 11:15:34 2014 +0200
@@ -52,16 +52,9 @@
         revsets.append(" + ").append(from);
       }
     }
-    try {
-      if (myProgressConsumer != null)
-        myProgressConsumer.consume(-1f, "hg log " + revsets);
-      return myRepo.log(myRoot)
-              .showCommitsFromAllBranches()
-              .withRevsets(revsets.toString())
-              .call();
-    } finally {
-      if (myProgressConsumer != null)
-        myProgressConsumer.consume(-1f, "hg log " + revsets + " finished");
-    }
+    return myRepo.log(myRoot)
+            .showCommitsFromAllBranches()
+            .withRevsets(revsets.toString())
+            .call();
   }
 }