# HG changeset patch # User Dmitry Neverov # Date 1312445078 -14400 # Node ID 8c10f5cec37d079e4dce247bad830a5531e45b98 # Parent 13f3e7d0c42c50c87cf0d3b3a8749cbbb62db6ff TW-17797 Fix merge-base calculation Add MergeBaseCommand and 2 its implementations. Implementation for hg 1.7+ uses revsets to get last common ancestors. Implementation for hg <1.7 calculates merge-base manually using information about parents. diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVersion.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVersion.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVersion.java Thu Aug 04 12:04:38 2011 +0400 @@ -14,7 +14,7 @@ private final int myThird; - HgVersion(int major, int minor, int third) { + public HgVersion(int major, int minor, int third) { myMajor = major; myMinor = minor; myThird = third; @@ -33,6 +33,11 @@ } + public boolean isEqualsOrGreaterThan(HgVersion other) { + return compareTo(other) >= 0; + } + + @Override public String toString() { return myMajor + "." + myMinor + "." + myThird; diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java Thu Aug 04 12:04:38 2011 +0400 @@ -31,11 +31,11 @@ */ public class BaseCommand { private final Settings mySettings; - private final String myWorkDirectory; + private final File myWorkDirectory; public BaseCommand(@NotNull final Settings settings, @NotNull File workingDir) { mySettings = settings; - myWorkDirectory = workingDir.getAbsolutePath(); + myWorkDirectory = workingDir; } @@ -43,13 +43,13 @@ return mySettings; } - public String getWorkDirectory() { + public File getWorkDirectory() { return myWorkDirectory; } protected GeneralCommandLine createCommandLine() { GeneralCommandLine cli = createCL(); - cli.setWorkDirectory(myWorkDirectory); + cli.setWorkDirectory(myWorkDirectory.getAbsolutePath()); cli.setPassParentEnvs(true); return cli; } @@ -81,6 +81,7 @@ * case when $PATH contains /bin and doesn't contain * and hg executable is set to 'hg'. To fix it - run hg using windows shell which expand * hg to hg.cmd correctly. + * @param cli command line in which to setup hg executable */ private void setupExecutable(GeneralCommandLine cli) { if (SystemInfo.isWindows && getSettings().getHgCommandPath().equals("hg")) { diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangeSet.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangeSet.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangeSet.java Thu Aug 04 12:04:38 2011 +0400 @@ -16,7 +16,6 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Date; @@ -30,7 +29,7 @@ @NotNull private Date myTimestamp; private String myDescription; private boolean myContainsFiles; - private List myParents; + private List myParents = new ArrayList(); public ChangeSet(final int revNumber, @NotNull final String id) { super(revNumber, id); @@ -61,9 +60,6 @@ } public void addParent(@NotNull ChangeSetRevision rev) { - if (myParents == null) { - myParents = new ArrayList(); - } myParents.add(rev); } @@ -94,10 +90,10 @@ } /** - * Returns parrents of this change set, or null if there were no parents. + * Returns parrents of this change set * @return see above */ - @Nullable + @NotNull public List getParents() { return myParents; } diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java Thu Aug 04 12:04:38 2011 +0400 @@ -30,6 +30,7 @@ private boolean myInLocalRepository = false; private ChangeSet myChangeSet; + private Integer myRevisionNumber; public IdentifyCommand(@NotNull Settings settings, @NotNull File workingDir) { super(settings, workingDir); @@ -43,17 +44,24 @@ myChangeSet = changeSet; } + public void setRevisionNumber(int revisionNumber) { + myRevisionNumber = revisionNumber; + } + public String execute() throws VcsException { GeneralCommandLine cli = createCL(); cli.addParameter("identify"); if (myInLocalRepository) { - cli.setWorkDirectory(this.getWorkDirectory()); + cli.setWorkDirectory(this.getWorkDirectory().getAbsolutePath()); } else { cli.addParameter(getSettings().getRepositoryUrl()); } if (myChangeSet != null) { cli.addParameter("--rev"); cli.addParameter(myChangeSet.getId()); + } else if (myRevisionNumber != null) { + cli.addParameter("--rev"); + cli.addParameter(myRevisionNumber.toString()); } ExecResult res = runCommand(cli); failIfNotEmptyStdErr(cli, res); diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Init.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Init.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Init.java Thu Aug 04 12:04:38 2011 +0400 @@ -20,7 +20,7 @@ } public void execute() throws VcsException { - new File(getWorkDirectory()).mkdirs(); + getWorkDirectory().mkdirs(); GeneralCommandLine cli = createCommandLine(); cli.addParameter("init"); runCommand(cli); diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java Thu Aug 04 12:04:38 2011 +0400 @@ -30,7 +30,10 @@ import java.util.Locale; public class LogCommand extends BaseCommand { + private final static Logger LOG = Logger.getInstance(LogCommand.class.getName()); + private final static String ZERO_PARENT_ID = "0000000000000000000000000000000000000000"; + private String myFromId; private String myToId; private ArrayList myPaths; @@ -43,6 +46,8 @@ private static final String DESCRIPTION_PREFIX = "description:"; private static final String FILES_PREFIX = "files:"; private String myBranchName; + private boolean myCalculateParents = true; + private String myRevsets; public LogCommand(@NotNull Settings settings, @NotNull File workingDir) { super(settings, workingDir); @@ -65,8 +70,16 @@ myLimit = limit; } - public void setBranchName(String branchName) { - myBranchName = branchName; + public void showCommitsFromAllBranches() { + myBranchName = null; + } + + public void setCalculateParents(boolean doCalculate) { + myCalculateParents = doCalculate; + } + + public void setRevsets(String revsets) { + myRevsets = revsets; } public List execute() throws VcsException { @@ -80,11 +93,13 @@ cli.addParameter(getSettings().getBranchName()); } cli.addParameter("-r"); - String from = myFromId; - if (from == null) from = "0"; - String to = myToId; - if (to == null) to = "tip"; - cli.addParameter(from + ":" + to); + if (myRevsets != null) { + cli.addParameter(myRevsets); + } else { + String from = myFromId != null ? myFromId : "0"; + String to = myToId != null ? myToId : "tip"; + cli.addParameter(from + ":" + to); + } if (myLimit != null) { cli.addParameter("--limit"); cli.addParameter(myLimit.toString()); @@ -96,7 +111,10 @@ } ExecResult res = runCommand(cli); - return parseChangeSets(res.getStdout()); + List csets = parseChangeSets(res.getStdout()); + if (myCalculateParents) + assignTrivialParents(csets); + return csets; } public static List parseChangeSets(final String stdout) { @@ -176,4 +194,29 @@ return result; } + + private void assignTrivialParents(List csets) throws VcsException { + for (ChangeSet cset : csets) { + if (cset.getParents().isEmpty()) { + int parentRevNumber = cset.getRevNumber() - 1; + String parentId = getIdOf(parentRevNumber); + cset.addParent(new ChangeSetRevision(parentRevNumber, parentId)); + } + } + } + + 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); + String output = identify.execute().trim(); + if (output.contains(" ")) { + return output.substring(0, output.indexOf(" ")); + } else { + return output; + } + } + } diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeBaseCommand.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/MergeBaseCommand.java Thu Aug 04 12:04:38 2011 +0400 @@ -0,0 +1,25 @@ +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; + +} diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-server/src/META-INF/build-server-plugin-mercurial.xml --- a/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml Thu Aug 04 12:04:38 2011 +0400 @@ -4,4 +4,5 @@ + diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandFactory.java Thu Aug 04 12:04:38 2011 +0400 @@ -0,0 +1,38 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +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.serverSide.ServerPaths; +import jetbrains.buildServer.vcs.VcsException; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +/** + * @author dmitry.neverov + */ +public class CommandFactory { + + //hg version which supports revsets + private final static HgVersion REVSET_HG_VERSION = new HgVersion(1, 7, 0); + + private final File myDefaultWorkingDir; + + + public CommandFactory(@NotNull final ServerPaths paths) { + myDefaultWorkingDir = new File(paths.getCachesDir(), "mercurial"); + } + + + @NotNull + public MergeBaseCommand createMergeBase(@NotNull Settings settings, @NotNull File workingDir) throws VcsException { + VersionCommand versionCommand = new VersionCommand(settings, myDefaultWorkingDir); + HgVersion hgVersion = versionCommand.execute(); + if (hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION)) + return new MergeBaseWithRevsets(settings, workingDir); + else + return new MergeBaseNoRevsets(settings, workingDir); + } + +} diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java --- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Thu Aug 04 12:04:38 2011 +0400 @@ -53,22 +53,26 @@ *

Working copy of repository is created in the $TEAMCITY_DATA_PATH/system/caches/hg_<hash code> folder. *

Personal builds (remote runs) are not yet supported, they require corresponding functionality from the IDE. */ -public class MercurialVcsSupport extends ServerVcsSupport implements LabelingSupport, VcsFileContentProvider, BranchSupport { +public class MercurialVcsSupport extends ServerVcsSupport implements LabelingSupport, VcsFileContentProvider, BranchSupport, + CollectChangesByCheckoutRules { private ConcurrentMap myWorkDirLocks= new ConcurrentHashMap(); private VcsManager myVcsManager; private File myDefaultWorkFolderParent; private MirrorManager myMirrorManager; private final PluginConfig myConfig; + private final CommandFactory myCommandFactory; public MercurialVcsSupport(@NotNull final VcsManager vcsManager, @NotNull final ServerPaths paths, @NotNull final SBuildServer server, @NotNull final EventDispatcher dispatcher, - @NotNull final PluginConfig config) { + @NotNull final PluginConfig config, + @NotNull final CommandFactory commandFactory) { myVcsManager = vcsManager; myDefaultWorkFolderParent = new File(paths.getCachesDir(), "mercurial"); myMirrorManager = new MirrorManager(myDefaultWorkFolderParent); myConfig = config; + myCommandFactory = commandFactory; dispatcher.addListener(new BuildServerAdapter() { @Override public void cleanupFinished() { @@ -497,12 +501,15 @@ VcsRoot branchRoot = createBranchRoot(root, branchName); String baseVersion = getCurrentVersion(root); String branchVersion = getCurrentVersion(branchRoot); - String branchPoint = getBranchPoint(settings, baseVersion, branchVersion); + String mergeBase = getMergeBase(settings, baseVersion, branchVersion); + + if (mergeBase == null) + return null; LogCommand lc = new LogCommand(settings, getWorkingDir(settings)); - lc.setFromRevId(new ChangeSetRevision(branchPoint).getId()); + lc.setFromRevId(new ChangeSetRevision(mergeBase).getId()); lc.setToRevId(new ChangeSetRevision(branchVersion).getId()); - lc.setBranchName(null);//do not limit output to particular branch, return all commits + lc.showCommitsFromAllBranches(); List changeSets = lc.execute(); if (changeSets.size() > 1) {//when branch points to the commit in original branch we get 1 cset String branchId = changeSets.get(1).getId(); @@ -524,90 +531,87 @@ public List collectChanges(@NotNull VcsRoot fromRoot, @NotNull String fromRootRevision, @NotNull VcsRoot toRoot, @Nullable String toRootRevision, @NotNull CheckoutRules checkoutRules) throws VcsException { - //we get all branches while clone, if vcs roots are related it is doesn't matter in which one search for branch point - Settings settings = createSettings(fromRoot); + Settings settings = createSettings(toRoot); syncRepository(settings); - String branchPoint = getBranchPoint(settings, fromRootRevision, toRootRevision); - return ((CollectChangesByCheckoutRules) getCollectChangesPolicy()).collectChanges(toRoot, branchPoint, toRootRevision, checkoutRules); + String toRevision = toRootRevision != null ? toRootRevision : getCurrentVersion(toRoot); + String mergeBase = getMergeBase(settings, fromRootRevision, toRevision); + if (mergeBase == null) + mergeBase = getMinusNthCommit(settings, 10); + return collectChanges(toRoot, mergeBase, toRootRevision, checkoutRules); } - private String getBranchPoint(@NotNull Settings settings, String branchOneRev, String branchTwoRev) throws VcsException { - if (branchOneRev.equals(branchTwoRev)) - return branchOneRev; - File workingDir = getWorkingDir(settings); - LogCommand lc = new LogCommand(settings, workingDir); - lc.setFromRevId(new ChangeSetRevision(branchOneRev).getId()); - lc.setToRevId(new ChangeSetRevision(branchTwoRev).getId()); - lc.setLimit(1); - List changeSets = lc.execute(); - ChangeSet cs = changeSets.get(0); - if (cs.isInitial()) { - return cs.getId(); - } else { - return cs.getParents().get(0).getId(); - } + @Nullable + private String getMergeBase(@NotNull Settings settings, @NotNull String revision1, @NotNull String revision2) throws VcsException { + return myCommandFactory.createMergeBase(settings, getWorkingDir(settings)).execute(revision1, revision2); } + + @NotNull + private String getMinusNthCommit(@NotNull Settings settings, int n) throws VcsException { + LogCommand log = new LogCommand(settings, getWorkingDir(settings)); + log.setFromRevId(settings.getBranchName()); + if (n > 0) + log.setLimit(n); + List changeSets = log.execute(); + return changeSets.get(changeSets.size() - 1).getId(); + } + + @NotNull public CollectChangesPolicy getCollectChangesPolicy() { - return new CollectChangesByCheckoutRules() { - @NotNull - public List collectChanges(@NotNull VcsRoot root, @NotNull String fromVersion, @Nullable String currentVersion, @NotNull CheckoutRules checkoutRules) throws VcsException { - Settings settings = createSettings(root); - syncRepository(settings); - - // first obtain changes between specified versions - List result = new ArrayList(); - if (currentVersion == null) return result; - - File workingDir = getWorkingDir(settings); - LogCommand lc = new LogCommand(settings, workingDir); - String fromId = new ChangeSetRevision(fromVersion).getId(); - lc.setFromRevId(fromId); - lc.setToRevId(new ChangeSetRevision(currentVersion).getId()); - List changeSets = lc.execute(); - if (changeSets.isEmpty()) { - return result; - } - - // invoke status command for each changeset and determine what files were modified in these changesets - StatusCommand st = new StatusCommand(settings, workingDir); - ChangeSet prev = new ChangeSet(fromVersion); - for (ChangeSet cur : changeSets) { - if (cur.getId().equals(fromId)) continue; // skip already reported changeset + return this; + } - String prevId = prev.getId(); - List curParents = cur.getParents(); - boolean merge = curParents != null && curParents.size() > 1; - if (curParents != null && !merge) { - prevId = curParents.get(0).getId(); - } + public List collectChanges(@NotNull VcsRoot root, @NotNull String fromVersion, @Nullable String currentVersion, @NotNull CheckoutRules checkoutRules) throws VcsException { + Settings settings = createSettings(root); + syncRepository(settings); - List modifiedFiles = new ArrayList(); - if (merge) { - modifiedFiles.addAll(computeModifiedFilesForMergeCommit(settings, cur)); - } else { - st.setFromRevId(prevId); - st.setToRevId(cur.getId()); - modifiedFiles = st.execute(); - } + // first obtain changes between specified versions + List result = new ArrayList(); + if (currentVersion == null) return result; - // changeset full version will be set into VcsChange structure and - // stored in database (note that getContent method will be invoked with this version) - List files = toVcsChanges(modifiedFiles, prev.getFullVersion(), cur.getFullVersion(), checkoutRules); - if (files.isEmpty() && !merge) continue; - ModificationData md = new ModificationData(cur.getTimestamp(), files, cur.getDescription(), cur.getUser(), root, cur.getFullVersion(), cur.getId()); - if (merge) { - md.setCanBeIgnored(false); - } - result.add(md); - prev = cur; - } + File workingDir = getWorkingDir(settings); + LogCommand lc = new LogCommand(settings, workingDir); + String fromId = new ChangeSetRevision(fromVersion).getId(); + lc.setFromRevId(fromId); + lc.setToRevId(new ChangeSetRevision(currentVersion).getId()); + List changeSets = lc.execute(); + if (changeSets.isEmpty()) { + return result; + } - return result; + // invoke status command for each changeset and determine what files were modified in these changesets + StatusCommand st = new StatusCommand(settings, workingDir); + ChangeSet prev = new ChangeSet(fromVersion); + for (ChangeSet cur : changeSets) { + if (cur.getId().equals(fromId)) continue; // skip already reported changeset + + List curParents = cur.getParents(); + boolean mergeCommit = curParents.size() > 1; + List modifiedFiles = new ArrayList(); + if (mergeCommit) { + modifiedFiles.addAll(computeModifiedFilesForMergeCommit(settings, cur)); + } else { + if (!curParents.isEmpty()) + st.setFromRevId(curParents.get(0).getId()); + st.setToRevId(cur.getId()); + modifiedFiles.addAll(st.execute()); } - }; + + // changeset full version will be set into VcsChange structure and + // stored in database (note that getContent method will be invoked with this version) + List files = toVcsChanges(modifiedFiles, prev.getFullVersion(), cur.getFullVersion(), checkoutRules); + if (files.isEmpty() && !mergeCommit) continue; + ModificationData md = new ModificationData(cur.getTimestamp(), files, cur.getDescription(), cur.getUser(), root, cur.getFullVersion(), cur.getId()); + if (mergeCommit) { + md.setCanBeIgnored(false); + } + result.add(md); + prev = cur; + } + + return result; } @NotNull diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseNoRevsets.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseNoRevsets.java Thu Aug 04 12:04:38 2011 +0400 @@ -0,0 +1,63 @@ +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 class MergeBaseNoRevsets implements MergeBaseCommand { + + private final Settings mySettings; + private final File myWorkingDir; + + + public MergeBaseNoRevsets(@NotNull final Settings settings, @NotNull File workingDir) { + mySettings = settings; + myWorkingDir = workingDir; + } + + + @Nullable + public String execute(@NotNull String revision1, @NotNull String revision2) { + if (revision1.equals(revision2)) + return revision1; + try { + List> edges = new ArrayList>(); + fillEdges(edges, getRevisionsReachableFrom(revision1)); + fillEdges(edges, getRevisionsReachableFrom(revision2)); + DAG dag = DAGs.createFromEdges(edges); + List 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 getRevisionsReachableFrom(@NotNull String revision) throws VcsException { + LogCommand log = new LogCommand(mySettings, myWorkingDir); + log.setFromRevId(new ChangeSetRevision(revision).getId()); + log.showCommitsFromAllBranches(); + log.setToRevId("0"); + return log.execute(); + } + + + private void fillEdges(List> edges, List csets) { + for (ChangeSet cset : csets) { + for (ChangeSetRevision parent : cset.getParents()) { + edges.add(Pair.create(cset.getId(), parent.getId())); + } + } + } +} diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseWithRevsets.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MergeBaseWithRevsets.java Thu Aug 04 12:04:38 2011 +0400 @@ -0,0 +1,36 @@ +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 class MergeBaseWithRevsets implements MergeBaseCommand { + + private final Settings mySettings; + private final File myWorkingDir; + + public MergeBaseWithRevsets(@NotNull Settings settings, @NotNull File workingDir) { + mySettings = settings; + myWorkingDir = workingDir; + } + + public String execute(@NotNull String revision1, @NotNull String revision2) throws VcsException { + try { + LogCommand log = new LogCommand(mySettings, myWorkingDir); + log.setRevsets("ancestor(" + new ChangeSetRevision(revision1).getId() + ", " + new ChangeSetRevision(revision2).getId() + ")"); + log.showCommitsFromAllBranches(); + log.setCalculateParents(false); + List csets = log.execute(); + return csets.isEmpty() ? null : csets.get(0).getId(); + } catch (VcsException e) { + return null; + } + } +} diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/BaseMercurialTestCase.java Thu Aug 04 12:04:38 2011 +0400 @@ -51,17 +51,13 @@ } protected VcsRootImpl createVcsRoot(@NotNull String repPath) throws IOException { - VcsRootImpl vcsRoot = new VcsRootImpl(1, Constants.VCS_NAME); - vcsRoot.addProperty(Constants.HG_COMMAND_PATH_PROP, new File(Util.getHgPath()).getAbsolutePath()); File repository = LocalRepositoryUtil.prepareRepository(repPath); - vcsRoot.addProperty(Constants.REPOSITORY_PROP, repository.getAbsolutePath()); - return vcsRoot; + return new VcsRootBuilder().repository(repository.getAbsolutePath()).build(); } protected VcsRootImpl createVcsRoot(@NotNull String repPath, @NotNull String branchName) throws IOException { - VcsRootImpl vcsRoot = createVcsRoot(repPath); - vcsRoot.addProperty(Constants.BRANCH_NAME_PROP, branchName); - return vcsRoot; + File repository = LocalRepositoryUtil.prepareRepository(repPath); + return new VcsRootBuilder().repository(repository.getAbsolutePath()).branch(branchName).build(); } protected void cleanRepositoryAfterTest(@NotNull String repPath) { diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Thu Aug 04 12:04:38 2011 +0400 @@ -63,7 +63,7 @@ File systemDir = myTempFiles.createTempDir(); myServerPaths = new ServerPaths(systemDir.getAbsolutePath(), systemDir.getAbsolutePath(), systemDir.getAbsolutePath()); assertTrue(new File(myServerPaths.getCachesDir()).mkdirs()); - myVcs = new MercurialVcsSupport((VcsManager)vcsManagerMock.proxy(), myServerPaths, (SBuildServer)serverMock.proxy(), dispatcher, createPluginConfig()); + myVcs = new MercurialVcsSupport((VcsManager)vcsManagerMock.proxy(), myServerPaths, (SBuildServer)serverMock.proxy(), dispatcher, createPluginConfig(), new CommandFactory(myServerPaths)); } protected String getTestDataPath() { @@ -82,7 +82,7 @@ } private List collectChanges(@NotNull VcsRoot vcsRoot, @NotNull String from, @NotNull String to, @NotNull CheckoutRules rules) throws VcsException { - return ((CollectChangesByCheckoutRules) myVcs.getCollectChangesPolicy()).collectChanges(vcsRoot, from, to, rules); + return myVcs.collectChanges(vcsRoot, from, to, rules); } public void test_collect_changes_between_two_same_roots() throws Exception { @@ -92,6 +92,13 @@ do_check_for_collect_changes(changes); } + public void test_collect_changes_from_non_existing_revision() throws Exception { + VcsRootImpl vcsRoot = createVcsRoot(simpleRepo()); + VcsRootImpl sameVcsRoot = createVcsRoot(simpleRepo()); + List changes = myVcs.collectChanges(vcsRoot, "0:9875b412a789", sameVcsRoot, "3:9522278aa38d", new CheckoutRules("")); + assertFalse(changes.isEmpty());//should return some changes from the toRoot + } + public void test_collect_changes() throws Exception { VcsRootImpl vcsRoot = createVcsRoot(simpleRepo()); List changes = collectChanges(vcsRoot, "0:9875b412a788", "3:9522278aa38d", new CheckoutRules("")); diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java Thu Aug 04 12:04:38 2011 +0400 @@ -1,5 +1,6 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial; +import java.io.File; import java.io.IOException; /** @@ -14,7 +15,7 @@ if (providedHg != null) { return providedHg; } else { - return "mercurial-tests/testData/bin/hg.exe"; + return new File("mercurial-tests/testData/bin/hg.exe").getAbsolutePath(); } } diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsRootBuilder.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsRootBuilder.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsRootBuilder.java Thu Aug 04 12:04:38 2011 +0400 @@ -13,6 +13,7 @@ private String myRepository; private String myUsername; private String myPassword; + private String myBranch; private long myRootId = 1L; public VcsRootImpl build() throws IOException { @@ -21,6 +22,7 @@ vcsRoot.addProperty(Constants.HG_COMMAND_PATH_PROP, Util.getHgPath()); vcsRoot.addProperty(Constants.USERNAME, myUsername); vcsRoot.addProperty(Constants.PASSWORD, myPassword); + vcsRoot.addProperty(Constants.BRANCH_NAME_PROP, myBranch); return vcsRoot; } @@ -43,6 +45,12 @@ } + public VcsRootBuilder branch(@NotNull String branch) { + myBranch = branch; + return this; + } + + public VcsRootBuilder rootId(long rootId) { myRootId = rootId; return this; diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTest.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTest.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTest.java Thu Aug 04 12:04:38 2011 +0400 @@ -2,8 +2,7 @@ import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.openapi.util.SystemInfo; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.Constants; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.Util; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder; import jetbrains.buildServer.vcs.impl.VcsRootImpl; import junit.framework.TestCase; import org.testng.annotations.Test; @@ -18,9 +17,7 @@ public class BaseCommandTest extends TestCase { public void should_quote_command_line_arguments() throws IOException { - VcsRootImpl root = new VcsRootImpl(1, "rootForTest"); - root.addProperty(Constants.REPOSITORY_PROP, "http://some.org/repo.hg"); - root.addProperty(Constants.HG_COMMAND_PATH_PROP, Util.getHgPath()); + VcsRootImpl root = new VcsRootBuilder().repository("http://some.org/repo.hg").build(); File workingDir = new File("some dir"); Settings settings = new Settings(root); diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommandTest.java Thu Aug 04 12:04:38 2011 +0400 @@ -42,7 +42,6 @@ assertEquals(toId, changeSet.getId()); assertEquals("pavel@localhost", changeSet.getUser()); assertEquals("dir1 created", changeSet.getDescription()); - assertNull(changeSet.getParents()); } public void testMoreThanOneChangeSet() throws Exception { diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommandTest.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommandTest.java Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommandTest.java Thu Aug 04 12:04:38 2011 +0400 @@ -4,12 +4,14 @@ import jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder; import jetbrains.buildServer.vcs.impl.VcsRootImpl; import junit.framework.TestCase; +import org.testng.annotations.Test; import java.io.File; /** * @author dmitry.neverov */ +@Test public class VersionCommandTest extends TestCase { public void test() throws Exception { diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-tests/src/testng.xml --- a/mercurial-tests/src/testng.xml Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-tests/src/testng.xml Thu Aug 04 12:04:38 2011 +0400 @@ -12,6 +12,8 @@ + + diff -r 13f3e7d0c42c -r 8c10f5cec37d mercurial-tests/testData/README --- a/mercurial-tests/testData/README Mon Aug 01 16:38:05 2011 +0400 +++ b/mercurial-tests/testData/README Thu Aug 04 12:04:38 2011 +0400 @@ -1,3 +1,27 @@ +rep1 history: +@ 10:9c6a6b4aede0 Multiline description tip +| +| o 9:9babcf2d5705 name with space branch name with space +| | +| o 8:04c3ae4c6312 file modified test_branch +| | +| o 7:376dcf05cd2a new file added in the test_branch test_branch +|/ +o 6:b9deb9a1c6f4 files with spaces added +| +o 5:1d2cc6f3bc29 modified in subdir +| +o 4:b06a290a363b file modified +| +o 3:9522278aa38d file removed +| +o 2:7209b1f1d793 file4.txt added +| +o 1:1d446e82d356 new file added +| +o 0:9875b412a788 dir1 created + + rep2 history: @ 18:df04faa7575a merge personal-branch tip