changeset 57:99e757f2527b

branches support
author Pavel.Sher
date Sun, 26 Oct 2008 16:23:50 +0300
parents 45366338965b
children d1ed856e38ea
files mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Constants.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/CloneCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.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/UpdateCommand.java mercurial-server/resources/buildServerResources/mercurialSettings.jsp mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommandTest.java mercurial-tests/testData/patch3/after/dir with space/file with space.txt mercurial-tests/testData/patch3/after/dir1/file1.txt mercurial-tests/testData/patch3/after/dir1/file3.txt mercurial-tests/testData/patch3/after/dir1/subdir/file2.txt mercurial-tests/testData/patch3/after/file_in_branch.txt mercurial-tests/testData/patch4/after/dir with space/file with space.txt mercurial-tests/testData/patch4/after/dir1/file1.txt mercurial-tests/testData/patch4/after/dir1/file3.txt mercurial-tests/testData/patch4/after/dir1/subdir/file2.txt mercurial-tests/testData/patch4/after/file_in_branch.txt mercurial-tests/testData/patch4/before/dir with space/file with space.txt mercurial-tests/testData/patch4/before/dir1/file1.txt mercurial-tests/testData/patch4/before/dir1/file3.txt mercurial-tests/testData/patch4/before/dir1/subdir/file2.txt mercurial-tests/testData/patch4/before/file_in_branch.txt mercurial-tests/testData/rep1/hg/branch.cache mercurial-tests/testData/rep1/hg/store/00changelog.i mercurial-tests/testData/rep1/hg/store/00manifest.i mercurial-tests/testData/rep1/hg/store/data/file__in__branch.txt.i mercurial-tests/testData/rep1/hg/store/undo
diffstat 36 files changed, 320 insertions(+), 39 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Constants.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Constants.java	Sun Oct 26 16:23:50 2008 +0300
@@ -20,6 +20,7 @@
 public interface Constants {
   String VCS_NAME = "mercurial";
   String REPOSITORY_PROP = "repositoryPath";
+  String BRANCH_NAME_PROP = "branchName";
   String HG_COMMAND_PATH_PROP = "hgCommandPath";
   String USERNAME = "username";
   String PASSWORD = VcsRoot.SECURE_PROPERTY_PREFIX + "password";
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java	Sun Oct 26 16:23:50 2008 +0300
@@ -11,19 +11,29 @@
  */
 public class BaseCommand {
   private Settings mySettings;
+  private String myWorkDirectory;
 
   public BaseCommand(@NotNull final Settings settings) {
     mySettings = settings;
+    myWorkDirectory = settings.getLocalRepositoryDir().getAbsolutePath();
   }
 
   public Settings getSettings() {
     return mySettings;
   }
 
+  /**
+   * Sets new working directory, by default working directory is taken from the Settings#getLocalRepositoryDir
+   * @param workDirectory work dir
+   */
+  public void setWorkDirectory(final String workDirectory) {
+    myWorkDirectory = workDirectory;
+  }
+
   protected GeneralCommandLine createCommandLine() {
     GeneralCommandLine cli = new GeneralCommandLine();
     cli.setExePath(getSettings().getHgCommandPath());
-    cli.setWorkDirectory(getSettings().getWorkingDir().getAbsolutePath());
+    cli.setWorkDirectory(myWorkDirectory);
     return cli;
   }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BranchesCommand.java	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,44 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import com.intellij.execution.configurations.GeneralCommandLine;
+import jetbrains.buildServer.ExecResult;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/**
+ * @author Pavel.Sher
+ *         Date: 26.10.2008
+ */
+public class BranchesCommand extends BaseCommand {
+  public BranchesCommand(@NotNull final Settings settings) {
+    super(settings);
+  }
+
+  /**
+   * Returns map of branch name to latest changeset in that branch
+   * @return see above
+   * @throws jetbrains.buildServer.vcs.VcsException if error occurs
+   */
+  public Map<String, ChangeSet> execute() throws VcsException {
+    GeneralCommandLine cli = createCommandLine();
+    cli.addParameter("branches");
+    ExecResult res = runCommand(cli);
+    String stdout = res.getStdout();
+    Map<String, ChangeSet> result = new HashMap<String, ChangeSet>();
+    Pattern branchPattern = Pattern.compile("(.*)[\\s]+([0-9]+:[A-Za-z0-9]+).*");
+    for (String line: stdout.split("[\r\n]+")) {
+      Matcher matcher = branchPattern.matcher(line);
+      if (matcher.matches()) {
+        String branchName = matcher.group(1).trim();
+        String changeId = matcher.group(2).trim();
+        result.put(branchName, new ChangeSet(changeId));
+      }
+    }
+    return result;
+  }
+}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java	Sun Oct 26 16:23:50 2008 +0300
@@ -25,9 +25,19 @@
   private String myDestDir;
   private String myToId;
   private boolean myUpdateWorkingDir = true;
+  private String myRepositoryPath;
 
   public CloneCommand(@NotNull final Settings settings) {
     super(settings);
+    myRepositoryPath = getSettings().getRepository();
+  }
+
+  /**
+   * Sets repository to clone, by default uses repository from the specified settings
+   * @param repo repository path
+   */
+  public void setRepositoryPath(@NotNull String repo) {
+    myRepositoryPath = repo;
   }
 
   public void setDestDir(@NotNull String destDir) {
@@ -57,7 +67,7 @@
     if (!myUpdateWorkingDir) {
       cli.addParameter("-U");
     }
-    cli.addParameter(getSettings().getRepository());
+    cli.addParameter(myRepositoryPath);
     cli.addParameter(dir.getName());
 
     runCommand(cli, 24*3600); // some repositories are quite large, we set timeout to 24 hours 
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java	Sun Oct 26 16:23:50 2008 +0300
@@ -17,7 +17,6 @@
 
 import com.intellij.execution.configurations.GeneralCommandLine;
 import jetbrains.buildServer.ExecResult;
-import jetbrains.buildServer.util.StringUtil;
 import jetbrains.buildServer.vcs.VcsException;
 import org.jetbrains.annotations.NotNull;
 
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Sun Oct 26 16:23:50 2008 +0300
@@ -58,6 +58,8 @@
   public List<ChangeSet> execute() throws VcsException {
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("log");
+    cli.addParameter("-b");
+    cli.addParameter(getSettings().getBranchName());
     cli.addParameter("-r");
     String from = myFromId;
     if (from == null) from = "0";
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java	Sun Oct 26 16:23:50 2008 +0300
@@ -9,13 +9,22 @@
  * @author pavel
  */
 public class PushCommand extends BaseCommand {
+  private boolean myForced;
+
   public PushCommand(@NotNull final Settings settings) {
     super(settings);
   }
 
+  public void setForce(boolean force) {
+    myForced = force;
+  }
+
   public void execute() throws VcsException {
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("push");
+    if (myForced) {
+      cli.addParameter("-f");
+    }
     cli.addParameter(getSettings().getPushUrl());
     ExecResult res = runCommand(cli);
     failIfNotEmptyStdErr(cli, res);
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java	Sun Oct 26 16:23:50 2008 +0300
@@ -36,11 +36,14 @@
   private File myWorkFolderParentDir;
   private String myUsername;
   private String myPassword;
+  private String myBranchName;
+  private static final String DEFAULT_BRANCH_NAME = "default";
 
   public Settings(@NotNull File workFolderParentDir, @NotNull VcsRoot vcsRoot) {
     myWorkFolderParentDir = workFolderParentDir;
     setRepository(vcsRoot.getProperty(Constants.REPOSITORY_PROP));
     setHgCommandPath(vcsRoot.getProperty(Constants.HG_COMMAND_PATH_PROP));
+    myBranchName = vcsRoot.getProperty(Constants.BRANCH_NAME_PROP);
 
     myUsername = vcsRoot.getProperty(Constants.USERNAME);
     myPassword = vcsRoot.getProperty(Constants.PASSWORD);
@@ -63,6 +66,23 @@
   }
 
   /**
+   * Returns name of the branch to use (returns 'default' if no branch specified)
+   * @return see above
+   */
+  @NotNull
+  public String getBranchName() {
+    return StringUtil.isEmpty(myBranchName) ? DEFAULT_BRANCH_NAME : myBranchName;
+  }
+
+  /**
+   * Returns true if current branch is default branch
+   * @return see above
+   */
+  public boolean isDefaultBranch() {
+    return getBranchName().equals(DEFAULT_BRANCH_NAME);
+  }
+
+  /**
    * Returns path to hg command
    * @return path to hg command
    */
@@ -110,11 +130,11 @@
   }
 
   /**
-   * Returns repository working directory where all mercurial commands should be invoked
+   * Returns directory where repository is supposed to be cloned, i.e. working directory of cloned repository
    * @return repository working directory
    */
   @NotNull
-  public File getWorkingDir() {
+  public File getLocalRepositoryDir() {
     if (myWorkingDir != null) {
       return myWorkingDir;
     }
@@ -128,7 +148,7 @@
    */
   public boolean hasCopyOfRepository() {
     // need better way to check that repository copy is ok
-    return getWorkingDir().isDirectory() && new File(getWorkingDir(), ".hg").isDirectory();
+    return getLocalRepositoryDir().isDirectory() && new File(getLocalRepositoryDir(), ".hg").isDirectory();
   }
 
   public static String DEFAULT_WORK_DIR_PREFIX = "hg_";
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java	Sun Oct 26 16:23:50 2008 +0300
@@ -31,9 +31,16 @@
   }
 
   public void execute() throws VcsException {
+    // switch working directory to current branch
     GeneralCommandLine cli = createCommandLine();
     cli.addParameter("update");
     cli.addParameter("-C");
+    cli.addParameter(getSettings().getBranchName());
+    runCommand(cli);
+
+    cli = createCommandLine();
+    cli.addParameter("update");
+    cli.addParameter("-C");
     if (myToId != null) {
       cli.addParameter("-r");
       cli.addParameter(myToId);
--- a/mercurial-server/resources/buildServerResources/mercurialSettings.jsp	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-server/resources/buildServerResources/mercurialSettings.jsp	Sun Oct 26 16:23:50 2008 +0300
@@ -1,6 +1,13 @@
 <%@include file="/include.jsp"%>
 <%@ taglib prefix="props" tagdir="/WEB-INF/tags/props" %>
 <jsp:useBean id="propertiesBean" scope="request" type="jetbrains.buildServer.controllers.BasePropertiesBean"/>
+<script type="text/javascript">
+function updateBranchName(repoPath) {
+  if (repoPath.indexOf('#') != -1 && $('branchName').value == '') {
+    $('branchName').value = repoPath.substring(repoPath.indexOf('#')+1);
+  }
+}
+</script>
 <table class="runnerFormTable">
 
   <l:settingsGroup title="General Settings">
@@ -11,9 +18,13 @@
   </tr>
   <tr>
     <th><label for="repositoryPath">Pull changes from: <l:star/></label></th>
-    <td><props:textProperty name="repositoryPath" className="longField" />
+    <td><props:textProperty name="repositoryPath" className="longField" onchange="updateBranchName(this.value)"/>
       <span class="error" id="error_repositoryPath"></span></td>
   </tr>
+  <tr>
+    <th><label for="branchName">Branch name: </label></th>
+    <td><props:textProperty name="branchName" /></td>
+  </tr>
   </l:settingsGroup>
   <l:settingsGroup title="Authorization settings">
   <tr>
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Sun Oct 26 16:23:50 2008 +0300
@@ -50,7 +50,7 @@
  * <li>executes corresponding hg command
  * </ul>
  *
- * <p>Working copy of repository is created in the $TEAMCITY_DATA_PATH/system/caches/mercurial folder.
+ * <p>Working copy of repository is created in the $TEAMCITY_DATA_PATH/system/caches/hg_&lt;hash code> folder.
  * <p>Personal builds (remote runs) are not yet supported, they require corresponding functionality from the IDE.
  */
 public class MercurialVcsSupport extends VcsSupport implements CollectChangesByIncludeRule, LabelingSupport, AgentSideCheckoutAbility {
@@ -88,7 +88,8 @@
     List<ModificationData> result = new ArrayList<ModificationData>();
     Settings settings = createSettings(root);
     LogCommand lc = new LogCommand(settings);
-    lc.setFromRevId(new ChangeSet(fromVersion).getId());
+    String fromId = new ChangeSet(fromVersion).getId();
+    lc.setFromRevId(fromId);
     lc.setToRevId(new ChangeSet(currentVersion).getId());
     List<ChangeSet> changeSets = lc.execute();
     if (changeSets.isEmpty()) {
@@ -96,11 +97,12 @@
     }
 
     // invoke status command for each changeset and determine what files were modified in these changesets
-    Iterator<ChangeSet> it = changeSets.iterator();
-    ChangeSet prev = it.next(); // skip first changeset (cause it was already reported)
     StatusCommand st = new StatusCommand(settings);
-    while (it.hasNext()) {
-      ChangeSet cur = it.next();
+    ChangeSet prev = new ChangeSet(fromVersion);
+    for (int i=0; i<changeSets.size(); i++) {
+      ChangeSet cur = changeSets.get(i);
+      if (cur.getId().equals(fromId)) continue; // skip already reported changeset
+
       st.setFromRevId(prev.getId());
       st.setToRevId(cur.getId());
       List<ModifiedFile> modifiedFiles = st.execute();
@@ -210,9 +212,13 @@
     // we will return full version of the most recent change as current version
     syncClonedRepository(root);
     Settings settings = createSettings(root);
-    TipCommand lc = new TipCommand(settings);
-    ChangeSet changeSet = lc.execute();
-    return changeSet.getFullVersion();
+    BranchesCommand branches = new BranchesCommand(settings);
+    Map<String, ChangeSet> result = branches.execute();
+    if (!result.containsKey(settings.getBranchName())) {
+      throw new VcsException("Unable to find current version for the branch: " + settings.getBranchName());
+    }
+
+    return result.get(settings.getBranchName()).getFullVersion();
   }
 
   public String describeVcsRoot(final VcsRoot vcsRoot) {
@@ -328,12 +334,21 @@
   private void buildFullPatch(final Settings settings, @NotNull final ChangeSet toVer, final PatchBuilder builder)
     throws IOException, VcsException {
     CloneCommand cl = new CloneCommand(settings);
+    // clone from the local repository
+    cl.setRepositoryPath(settings.getLocalRepositoryDir().getAbsolutePath());
     cl.setToId(toVer.getId());
+    cl.setUpdateWorkingDir(false);
     File tempDir = FileUtil.createTempDirectory("mercurial", toVer.getId());
     try {
       final File repRoot = new File(tempDir, "rep");
       cl.setDestDir(repRoot.getAbsolutePath());
       cl.execute();
+
+      UpdateCommand up = new UpdateCommand(settings);
+      up.setWorkDirectory(repRoot.getAbsolutePath());
+      up.setToId(toVer.getId());
+      up.execute();
+
       buildPatchFromDirectory(builder, repRoot, new FileFilter() {
         public boolean accept(final File file) {
           return !(file.isDirectory() && ".hg".equals(file.getName()));
@@ -372,7 +387,7 @@
   // updates current working copy of repository by pulling changes from the repository specified in VCS root
   private void syncClonedRepository(final VcsRoot root) throws VcsException {
     Settings settings = createSettings(root);
-    File workDir = settings.getWorkingDir();
+    File workDir = settings.getLocalRepositoryDir();
     lockWorkDir(workDir);
     try {
       if (settings.hasCopyOfRepository()) {
@@ -443,7 +458,7 @@
     for (VcsRoot vcsRoot: myVcsManager.getAllRegisteredVcsRoots()) {
       if (getName().equals(vcsRoot.getVcsName())) {
         Settings s = createSettings(vcsRoot);
-        workDirs.remove(PathUtil.getCanonicalFile(s.getWorkingDir()));
+        workDirs.remove(PathUtil.getCanonicalFile(s.getLocalRepositoryDir()));
       }
     }
 
@@ -476,6 +491,7 @@
     tc.execute();
 
     PushCommand pc = new PushCommand(settings);
+    pc.setForce(true);
     pc.execute();
     return fixedTagname;
   }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java	Sun Oct 26 16:23:50 2008 +0300
@@ -30,11 +30,10 @@
     myVcsSupport = new MercurialAgentSideVcsSupport();
     myProgressLoggerMock = new Mock(BuildProgressLogger.class);
     myWorkDir = myTempFiles.createTempDir();
+    myProgressLoggerMock.stubs().method("message");
   }
 
   public void checkout_on_agent() throws IOException, VcsException {
-    myProgressLoggerMock.stubs().method("message");
-    
     VcsRoot vcsRoot = createVcsRoot();
     myVcsSupport.updateSources((BuildProgressLogger) myProgressLoggerMock.proxy(), myWorkDir, vcsRoot, "4:b06a290a363b", new IncludeRule(".", ".", null));
     File hgDir = new File(myWorkDir, ".hg");
@@ -45,9 +44,18 @@
     checkDirectoriesAreEqual(new File(getTestDataPath(), "cleanPatch1/after"), myWorkDir);
   }
 
+  public void checkout_on_agent_from_branch() throws IOException, VcsException {
+    VcsRoot vcsRoot = createVcsRoot("test_branch");
+    myVcsSupport.updateSources((BuildProgressLogger) myProgressLoggerMock.proxy(), myWorkDir, vcsRoot, "7:376dcf05cd2a", new IncludeRule(".", ".", null));
+    File hgDir = new File(myWorkDir, ".hg");
+    assertTrue(hgDir.isDirectory());
+
+    FileUtil.delete(hgDir);
+
+    checkDirectoriesAreEqual(new File(getTestDataPath(), "patch3/after"), myWorkDir);
+  }
+
   public void update_on_agent() throws IOException, VcsException {
-    myProgressLoggerMock.stubs().method("message");
-
     BuildProgressLogger logger = (BuildProgressLogger) myProgressLoggerMock.proxy();
 
     VcsRoot vcsRoot = createVcsRoot();
@@ -61,6 +69,20 @@
     checkDirectoriesAreEqual(new File(getTestDataPath(), "patch1/after"), myWorkDir);
   }
 
+  public void update_on_agent_from_branch() throws IOException, VcsException {
+    BuildProgressLogger logger = (BuildProgressLogger) myProgressLoggerMock.proxy();
+
+    VcsRoot vcsRoot = createVcsRoot("test_branch");
+    myVcsSupport.updateSources(logger, myWorkDir, vcsRoot, "7:376dcf05cd2a", new IncludeRule(".", ".", null));
+    File hgDir = new File(myWorkDir, ".hg");
+    assertTrue(hgDir.isDirectory());
+
+    myVcsSupport.updateSources(logger, myWorkDir, vcsRoot, "8:04c3ae4c6312", new IncludeRule(".", ".", null));
+    FileUtil.delete(hgDir);
+
+    checkDirectoriesAreEqual(new File(getTestDataPath(), "patch4/after"), myWorkDir);
+  }
+
   protected String getTestDataPath() {
     return "mercurial-tests/testData";
   }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java	Sun Oct 26 16:23:50 2008 +0300
@@ -4,6 +4,7 @@
 import jetbrains.buildServer.TempFiles;
 import jetbrains.buildServer.vcs.impl.VcsRootImpl;
 import jetbrains.buildServer.vcs.patches.PatchTestCase;
+import org.jetbrains.annotations.NotNull;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 
@@ -43,6 +44,12 @@
     return vcsRoot;
   }
 
+  protected VcsRootImpl createVcsRoot(@NotNull String branchName) throws IOException {
+    VcsRootImpl vcsRoot = createVcsRoot();
+    vcsRoot.addProperty(Constants.BRANCH_NAME_PROP, branchName);
+    return vcsRoot;
+  }
+
   private String getRepositoryPath() {
     return new File("mercurial-tests/testData/rep1").getAbsolutePath();
   }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Sun Oct 26 16:23:50 2008 +0300
@@ -18,6 +18,7 @@
 import com.intellij.execution.configurations.GeneralCommandLine;
 import jetbrains.buildServer.ExecResult;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandUtil;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
 import jetbrains.buildServer.serverSide.SBuildServer;
 import jetbrains.buildServer.serverSide.ServerPaths;
 import jetbrains.buildServer.vcs.*;
@@ -37,6 +38,7 @@
 @Test
 public class MercurialVcsSupportTest extends BaseMercurialTestCase {
   private MercurialVcsSupport myVcs;
+  private ServerPaths myServerPaths;
 
   @BeforeMethod
   protected void setUp() throws Exception {
@@ -49,23 +51,27 @@
     serverMock.stubs().method("getExecutor").will(myMockSupport.returnValue(executor));
 
     File systemDir = myTempFiles.createTempDir();
-    ServerPaths sp = new ServerPaths(systemDir.getAbsolutePath(), systemDir.getAbsolutePath());
-    assertTrue(new File(sp.getCachesDir()).mkdirs());
-    myVcs = new MercurialVcsSupport((VcsManager)vcsManagerMock.proxy(), sp, (SBuildServer)serverMock.proxy());
+    myServerPaths = new ServerPaths(systemDir.getAbsolutePath(), systemDir.getAbsolutePath());
+    assertTrue(new File(myServerPaths.getCachesDir()).mkdirs());
+    myVcs = new MercurialVcsSupport((VcsManager)vcsManagerMock.proxy(), myServerPaths, (SBuildServer)serverMock.proxy());
   }
 
   protected String getTestDataPath() {
     return "mercurial-tests/testData";
   }
 
-  public void testGetCurrentVersion() throws Exception {
+  public void test_get_current_version() throws Exception {
     VcsRootImpl vcsRoot = createVcsRoot();
 
     assertEquals(myVcs.getCurrentVersion(vcsRoot), "6:b9deb9a1c6f4");
     assertEquals("b9deb9a1c6f4", myVcs.getVersionDisplayName("6:b9deb9a1c6f4", vcsRoot));
+
+    assertEquals(myVcs.getCurrentVersion(createVcsRoot("test_branch")), "8:04c3ae4c6312");
+
+    assertEquals(myVcs.getCurrentVersion(createVcsRoot("name with space")), "9:9babcf2d5705");
   }
 
-  public void testCollectChanges() throws Exception {
+  public void test_collect_changes() throws Exception {
     VcsRootImpl vcsRoot = createVcsRoot();
 
     List<ModificationData> changes = myVcs.collectBuildChanges(vcsRoot, "0:9875b412a788", "3:9522278aa38d", new CheckoutRules(""));
@@ -96,7 +102,7 @@
     assertEquals(normalizePath(files3.get(0).getRelativeFileName()), "dir1/file4.txt");
   }
 
-  public void testCollectChangesWithCheckoutRules() throws Exception {
+  public void test_collect_changes_with_checkout_rules() throws Exception {
     VcsRootImpl vcsRoot = createVcsRoot();
 
     List<ModificationData> changes = myVcs.collectBuildChanges(vcsRoot, "0:9875b412a788", "3:9522278aa38d", new CheckoutRules("-:.\n+:dir1/subdir"));
@@ -108,7 +114,8 @@
     assertEquals(md.getDescription(), "modified in subdir");
   }
 
-  public void testBuildFullPatch() throws IOException, VcsException {
+  @Test(invocationCount = 3)
+  public void test_build_patch() throws IOException, VcsException {
     setName("cleanPatch1");
     VcsRootImpl vcsRoot = createVcsRoot();
 
@@ -121,7 +128,7 @@
     checkPatchResult(output.toByteArray());
   }
 
-  public void testBuildIncrementalPatch() throws IOException, VcsException {
+  public void test_build_incremental_patch() throws IOException, VcsException {
     setName("patch1");
     VcsRootImpl vcsRoot = createVcsRoot();
 
@@ -134,7 +141,7 @@
     checkPatchResult(output.toByteArray());
   }
 
-  public void testBuildIncrementalPatch_FileWithSpaceInChangeSet() throws IOException, VcsException {
+  public void test_build_incremental_patch_file_with_space() throws IOException, VcsException {
     setName("patch2");
     VcsRootImpl vcsRoot = createVcsRoot();
 
@@ -147,7 +154,7 @@
     checkPatchResult(output.toByteArray());
   }
 
-  public void testGetContent() throws IOException, VcsException {
+  public void test_get_content() throws IOException, VcsException {
     VcsRootImpl vcsRoot = createVcsRoot();
 
     byte[] content = myVcs.getContent("dir1/subdir/file2.txt", vcsRoot, "4:b06a290a363b");
@@ -156,7 +163,15 @@
     assertEquals(new String(content), "modified\r\nbbb");
   }
 
-  public void testTestConnection() throws IOException, VcsException {
+  public void test_get_content_in_branch() throws IOException, VcsException {
+    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+
+    byte[] content = myVcs.getContent("file_in_branch.txt", vcsRoot, "7:376dcf05cd2a");
+    content = myVcs.getContent("file_in_branch.txt", vcsRoot, "8:04c3ae4c6312");
+    assertEquals(new String(content), "file from the test_branch\r\nfile modified");
+  }
+
+  public void test_test_connection() throws IOException, VcsException {
     VcsRootImpl vcsRoot = createVcsRoot();
 
     System.out.println(myVcs.testConnection(vcsRoot));
@@ -169,7 +184,7 @@
     }
   }
 
-  public void testTagging() throws IOException, VcsException {
+  public void test_tag() throws IOException, VcsException {
     VcsRootImpl vcsRoot = createVcsRoot();
     cleanRepositoryAfterTest(); 
 
@@ -186,6 +201,89 @@
     assertTrue(res.getStdout().contains("1:1d446e82d356"));
   }
 
+  public void test_tag_in_branch() throws IOException, VcsException {
+    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+    cleanRepositoryAfterTest();
+
+    String actualTag = myVcs.label("branch_tag", "7:376dcf05cd2a", vcsRoot, new CheckoutRules(""));
+    assertEquals(actualTag, "branch_tag");
+
+    // check the tag is pushed to the parent repository
+    GeneralCommandLine cli = new GeneralCommandLine();
+    cli.setExePath(vcsRoot.getProperty(Constants.HG_COMMAND_PATH_PROP));
+    cli.setWorkDirectory(vcsRoot.getProperty(Constants.REPOSITORY_PROP));
+    cli.addParameter("tags");
+    ExecResult res = CommandUtil.runCommand(cli);
+    assertTrue(res.getStdout().contains("branch_tag"));
+    assertTrue(res.getStdout().contains("7:376dcf05cd2a"));
+  }
+
+  public void test_collect_changes_in_branch() throws Exception {
+    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+
+    // fromVersion(6:b9deb9a1c6f4) is not in the branch (it is in the default branch)
+    List<ModificationData> changes = myVcs.collectBuildChanges(vcsRoot, "6:b9deb9a1c6f4", "7:376dcf05cd2a", new CheckoutRules(""));
+    assertEquals(1, changes.size());
+
+    ModificationData md1 = changes.get(0);
+    assertEquals(md1.getVersion(), "7:376dcf05cd2a");
+    assertEquals(md1.getDescription(), "new file added in the test_branch");
+    List<VcsChange> files1 = md1.getChanges();
+    assertEquals(1, files1.size());
+    assertEquals(VcsChangeInfo.Type.ADDED, files1.get(0).getType());
+    assertEquals(normalizePath(files1.get(0).getRelativeFileName()), "file_in_branch.txt");
+
+    changes = myVcs.collectBuildChanges(vcsRoot, "7:376dcf05cd2a", "8:04c3ae4c6312", new CheckoutRules(""));
+    assertEquals(1, changes.size());
+
+    md1 = changes.get(0);
+    assertEquals(md1.getVersion(), "8:04c3ae4c6312");
+    assertEquals(md1.getDescription(), "file modified");
+  }
+
+  public void test_full_patch_from_branch() throws IOException, VcsException {
+    setName("patch3");
+    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+
+    final ByteArrayOutputStream output = new ByteArrayOutputStream();
+    final PatchBuilderImpl builder = new PatchBuilderImpl(output);
+
+    myVcs.buildPatch(vcsRoot, null, "7:376dcf05cd2a", builder, new CheckoutRules(""));
+    builder.close();
+
+    checkPatchResult(output.toByteArray());
+  }
+
+  public void test_incremental_patch_from_branch() throws IOException, VcsException {
+    setName("patch4");
+    VcsRootImpl vcsRoot = createVcsRoot("test_branch");
+
+    final ByteArrayOutputStream output = new ByteArrayOutputStream();
+    final PatchBuilderImpl builder = new PatchBuilderImpl(output);
+
+    myVcs.buildPatch(vcsRoot, "7:376dcf05cd2a", "8:04c3ae4c6312", builder, new CheckoutRules(""));
+    builder.close();
+
+    checkPatchResult(output.toByteArray());
+  }
+
+  @Test(enabled = false)
+  public void support_anchor_branch_notation() throws IOException {
+    VcsRootImpl vcsRoot = createVcsRoot();
+    String repPath = vcsRoot.getProperty(Constants.REPOSITORY_PROP);
+    vcsRoot.addProperty(Constants.REPOSITORY_PROP, repPath + "#test_branch");
+    Settings settings = new Settings(new File(myServerPaths.getCachesDir()), vcsRoot);
+    assertEquals("test_branch", settings.getBranchName());
+
+    vcsRoot.addProperty(Constants.REPOSITORY_PROP, repPath + "#");
+    settings = new Settings(new File(myServerPaths.getCachesDir()), vcsRoot);
+    assertEquals("default", settings.getBranchName());
+
+    vcsRoot.addProperty(Constants.REPOSITORY_PROP, repPath);
+    settings = new Settings(new File(myServerPaths.getCachesDir()), vcsRoot);
+    assertEquals("default", settings.getBranchName());
+  }
+
   private Object normalizePath(final String path) {
     return path.replace(File.separatorChar, '/');
   }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java	Sun Oct 26 16:23:50 2008 +0300
@@ -41,7 +41,7 @@
     settings.setWorkingDir(new File(parentDir, "rep").getAbsoluteFile());
     try {
       CloneCommand cl = new CloneCommand(settings);
-      cl.setDestDir(settings.getWorkingDir().getAbsolutePath());
+      cl.setDestDir(settings.getLocalRepositoryDir().getAbsolutePath());
       cl.execute();
 
       return executor.execute(settings);
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommandTest.java	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommandTest.java	Sun Oct 26 16:23:50 2008 +0300
@@ -15,12 +15,13 @@
  */
 package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
 
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+import org.testng.annotations.Test;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.List;
-import org.jetbrains.annotations.NotNull;
-import org.testng.annotations.Test;
-import jetbrains.buildServer.vcs.VcsException;
 
 @Test
 public class StatusCommandTest extends BaseCommandTestCase {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch3/after/dir with space/file with space.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,1 @@
+some text
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch3/after/dir1/file1.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,1 @@
+aaa
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch3/after/dir1/file3.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,2 @@
+ccc
+ddd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch3/after/dir1/subdir/file2.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,2 @@
+modified
+bbb
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch3/after/file_in_branch.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,1 @@
+file from the test_branch
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch4/after/dir with space/file with space.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,1 @@
+some text
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch4/after/dir1/file1.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,1 @@
+aaa
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch4/after/dir1/file3.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,2 @@
+ccc
+ddd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch4/after/dir1/subdir/file2.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,2 @@
+modified
+bbb
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch4/after/file_in_branch.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,2 @@
+file from the test_branch
+file modified
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch4/before/dir with space/file with space.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,1 @@
+some text
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch4/before/dir1/file1.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,1 @@
+aaa
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch4/before/dir1/file3.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,2 @@
+ccc
+ddd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch4/before/dir1/subdir/file2.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,2 @@
+modified
+bbb
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/testData/patch4/before/file_in_branch.txt	Sun Oct 26 16:23:50 2008 +0300
@@ -0,0 +1,1 @@
+file from the test_branch
\ No newline at end of file
--- a/mercurial-tests/testData/rep1/hg/branch.cache	Fri Oct 24 13:38:59 2008 +0400
+++ b/mercurial-tests/testData/rep1/hg/branch.cache	Sun Oct 26 16:23:50 2008 +0300
@@ -1,2 +1,4 @@
-b9deb9a1c6f43e8a3b9f7d12c1ec468352b4397e 6
+9babcf2d5705e64e85375516be4ede87baf9ce82 9
 b9deb9a1c6f43e8a3b9f7d12c1ec468352b4397e default
+04c3ae4c631244646c017d503be49fe62e9aea9c test_branch
+9babcf2d5705e64e85375516be4ede87baf9ce82 name with space
Binary file mercurial-tests/testData/rep1/hg/store/00changelog.i has changed
Binary file mercurial-tests/testData/rep1/hg/store/00manifest.i has changed
Binary file mercurial-tests/testData/rep1/hg/store/data/file__in__branch.txt.i has changed
Binary file mercurial-tests/testData/rep1/hg/store/undo has changed