Mercurial > hg > mercurial
changeset 564:d012388935fb Faradi-7.1.x
TW-26379 optimize changes collecting
Take revisions of all branches into account
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgRepo.java Wed Mar 06 15:49:37 2013 +0400 @@ -179,6 +179,10 @@ } } + public String getHgPath() { + return myHgPath; + } + @Override public String toString() { return myWorkingDir.getAbsolutePath();
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/HgVcsRoot.java Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/HgVcsRoot.java Wed Mar 06 15:49:37 2013 +0400 @@ -157,4 +157,8 @@ public String getSimplifiedName() { return myRoot.getSimplifiedName(); } + + public VcsRoot getOriginalRoot() { + return myRoot; + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LoadDagCommand.java Wed Mar 06 15:49:37 2013 +0400 @@ -0,0 +1,52 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import com.intellij.openapi.util.Pair; +import jetbrains.buildServer.util.StringUtil; +import jetbrains.buildServer.vcs.VcsException; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class LoadDagCommand extends VcsRootCommand { + + private final File myDagLogTemplate; + + public LoadDagCommand(@NotNull CommandSettings commandSettings, + @NotNull String hgPath, + @NotNull File workingDir, + @NotNull AuthSettings authSettings, + @NotNull File dagLogTemplate) { + super(commandSettings, hgPath, workingDir, authSettings); + myDagLogTemplate = dagLogTemplate; + } + + @NotNull + public List<Pair<String, String>> call() throws VcsException { + List<Pair<String, String>> edges = new ArrayList<Pair<String, String>>(); + MercurialCommandLine cli = createCommandLine(); + cli.addParameter("log"); + cli.addParameter("--style=" + myDagLogTemplate.getAbsolutePath()); + CommandResult res = runCommand(cli); + String output = res.getStdout(); + String fromNode = null; + for (String line : StringUtil.splitByLines(output)) { + String[] revs = line.split(" "); + if (revs.length == 0) + continue; + if (fromNode != null) { + edges.add(Pair.create(fromNode, revs[0])); + fromNode = null; + } + if (revs.length == 1) { + fromNode = revs[0]; + } else { + edges.add(Pair.create(revs[0], revs[1])); + if (revs.length == 3) + edges.add(Pair.create(revs[0], revs[2])); + } + } + return edges; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-server/resources/buildServerResources/dag.template Wed Mar 06 15:49:37 2013 +0400 @@ -0,0 +1,2 @@ +changeset = '{node|short} {parents}\n' +parent = '{node|short} ' \ No newline at end of file
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Wed Mar 06 15:49:37 2013 +0400 @@ -39,6 +39,7 @@ import java.io.IOException; import java.util.*; +import static java.util.Arrays.asList; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.HgFileUtil.deleteDir; /** @@ -528,6 +529,9 @@ @NotNull CheckoutRules rules) throws VcsException { Set<String> reportedCsetIds = new HashSet<String>(); List<ModificationData> changes = new ArrayList<ModificationData>(); + HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); + OperationContext ctx = new OperationContext(this, myRepoFactory, myHgPathProvider, fromState, toState); + ctx.syncRepository(hgRoot); for (Map.Entry<String, String> entry : toState.getBranchRevisions().entrySet()) { String branch = entry.getKey(); String toRevision = entry.getValue(); @@ -536,7 +540,9 @@ fromRevision = fromState.getBranchRevisions().get(fromState.getDefaultBranchName()); if (toRevision.equals(fromRevision)) continue; - List<ModificationData> branchChanges = collectChanges(root, fromRevision, toRevision, rules); + + Collection<String> fromRevisions = ctx.getFromRevisionsForBranch(hgRoot, fromRevision, toRevision); + List<ModificationData> branchChanges = collectChanges(hgRoot, fromRevisions, toRevision, rules); for (ModificationData change : branchChanges) { if (reportedCsetIds.add(change.getVersion())) changes.add(change); @@ -555,7 +561,7 @@ String mergeBase = getMergeBase(hgRoot, fromRootRevision, toRevision); if (mergeBase == null) return Collections.emptyList(); - return collectChanges(toRoot, mergeBase, toRootRevision, checkoutRules); + return collectChanges(hgRoot, asList(mergeBase), toRootRevision, checkoutRules); } @@ -593,9 +599,14 @@ public List<ModificationData> collectChanges(@NotNull VcsRoot root, @NotNull String fromVersion, @Nullable String currentVersion, @NotNull CheckoutRules checkoutRules) throws VcsException { HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); syncRepository(hgRoot); + return collectChanges(hgRoot, asList(fromVersion), currentVersion, checkoutRules); + } + + + private List<ModificationData> collectChanges(@NotNull HgVcsRoot root, @NotNull Collection<String> fromVersions, @Nullable String currentVersion, @NotNull CheckoutRules checkoutRules) throws VcsException { List<ModificationData> result = new ArrayList<ModificationData>(); - for (ChangeSet cset : getChangesets(hgRoot, fromVersion, currentVersion)) { - result.add(createModificationData(cset, root, checkoutRules)); + for (ChangeSet cset : getChangesets(root, fromVersions, currentVersion)) { + result.add(createModificationData(cset, root.getOriginalRoot(), checkoutRules)); } return result; } @@ -627,20 +638,25 @@ @NotNull - private List<ChangeSet> getChangesets(@NotNull final HgVcsRoot root, @NotNull final String fromVersion, @Nullable final String toVersion) throws VcsException { + private List<ChangeSet> getChangesets(@NotNull final HgVcsRoot root, + @NotNull final Collection<String> fromVersions, + @Nullable final String toVersion) throws VcsException { if (toVersion == null) return Collections.emptyList(); - String fromCommit = new ChangeSetRevision(fromVersion).getId(); + List<String> fromCommits = new ArrayList<String>(); + for (String fromVersion : fromVersions) { + fromCommits.add(new ChangeSetRevision(fromVersion).getId()); + } String toCommit = new ChangeSetRevision(toVersion).getId(); try { List<ChangeSet> changesets = createRepo(root).collectChanges(root) - .fromRevision(fromCommit) + .fromRevision(fromCommits) .toRevision(toCommit) .call(); Iterator<ChangeSet> iter = changesets.iterator(); while (iter.hasNext()) { ChangeSet cset = iter.next(); - if (cset.getId().equals(fromCommit)) + if (fromVersions.contains(cset.getId())) iter.remove();//skip already reported changes } return changesets; @@ -727,7 +743,7 @@ return label.replace(':', '_').replace('\r', '_').replace('\n', '_'); } - private File getWorkingDir(HgVcsRoot root) { + File getWorkingDir(HgVcsRoot root) { File customDir = root.getCustomWorkingDir(); return customDir != null ? customDir : myMirrorManager.getMirrorDir(root.getRepository()); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/OperationContext.java Wed Mar 06 15:49:37 2013 +0400 @@ -0,0 +1,180 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import com.intellij.openapi.util.Pair; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.AuthSettings; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot; +import jetbrains.buildServer.util.graph.*; +import jetbrains.buildServer.vcs.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.*; + +import static java.util.Collections.singleton; + +public class OperationContext { + + private final MercurialVcsSupport myVcs; + private final RepoFactory myRepoFactory; + private final HgPathProvider myHgPathProvider; + private final RepositoryState myFromState; + private final RepositoryState myToState; + private final Map<VcsRootKey, DAG<String>> myDags = new HashMap<VcsRootKey, DAG<String>>(); + private Set<File> mySyncedDirs = new HashSet<File>(); + private Map<File, ServerHgRepo> myRepos = new HashMap<File, ServerHgRepo>(); + + public OperationContext(@NotNull MercurialVcsSupport vcs, + @NotNull RepoFactory repoFactory, + @NotNull HgPathProvider hgPathProvider, + @NotNull RepositoryState fromState, + @NotNull RepositoryState toState) { + myVcs = vcs; + myRepoFactory = repoFactory; + myHgPathProvider = hgPathProvider; + myFromState = fromState; + myToState = toState; + } + + public void syncRepository(@NotNull final HgVcsRoot root) throws VcsException { + File dir = myVcs.getWorkingDir(root); + if (mySyncedDirs.contains(dir)) + return; + myVcs.syncRepository(root); + mySyncedDirs.add(dir); + } + + @NotNull + public ServerHgRepo createRepo(@NotNull File workingDir, @NotNull String hgPath, @NotNull AuthSettings authSettings) throws VcsException { + ServerHgRepo repo = myRepos.get(workingDir); + if (repo != null) + return repo; + repo = myRepoFactory.create(workingDir, hgPath, authSettings); + myRepos.put(workingDir, repo); + return repo; + } + + /** + * Collecting changes is per branch, but we should take revisions of other branches + * into account otherwise plugin can report redundant changes. Consider the following graph: + * + * default + * | topic + * | | + * V V + *103 + * | 102 + * | | + *101| + * |\| + * | 100 + * | | + *99 | + *..... + * | | + * |/ + * 1 + * + * Let's say plugin collects changes between states {default:99, topic:100} and {default: 103, topic:102}. + * If we run hg log -r 'ancestors(103)-ancestors(99)' it will return changes [1..103]. But changes + * [1..100] were probably already reported and TeamCity will not persist them. + * + * This method detects such a cases and returns (99, 100) for 103. + */ + @NotNull + public Collection<String> getFromRevisionsForBranch(@NotNull HgVcsRoot root, + @NotNull String fromRevision, + @NotNull String toRevision) throws VcsException { + syncRepository(root); + ServerHgRepo repo = createRepo(myVcs.getWorkingDir(root), myHgPathProvider.getHgPath(root), root.getAuthSettings()); + + if (!repo.supportRevsets()) + return singleton(fromRevision); + + Set<String> fromRevisions = new HashSet<String>(); + if (myToState.getBranchRevisions().size() > 1) { + VcsRootKey rootKey = VcsRootKey.create(root); + DAG<String> dag = myDags.get(rootKey); + if (dag == null) { + dag = repo.loadDag(); + myDags.put(rootKey, dag); + } + FindIntervalVisitor visitor = new FindIntervalVisitor(dag, myFromState.getBranchRevisions().values()); + dag.breadthFirstSearch(toRevision, visitor); + fromRevisions.addAll(visitor.getEndpoints()); + } else { + fromRevisions.add(fromRevision); + } + + if (fromRevisions.isEmpty()) + fromRevisions.add(toRevision); + + return fromRevisions; + } + + + private final static class VcsRootKey extends Pair<String, String> { + private VcsRootKey(@NotNull String repositoryUrl, @Nullable String subrepoPath) { + super(repositoryUrl, subrepoPath); + } + + private static VcsRootKey create(@NotNull VcsRoot root) { + return new VcsRootKey(root.getProperty(Constants.REPOSITORY_PROP), null); + } + } + + + private final static class FindIntervalVisitor extends BFSVisitorAdapter<String> { + + private final DAG<String> myDag; + private final Collection<String> myUninterestingRevisions; + private final Set<String> myVisitedUninterestingRevisions = new HashSet<String>(); + private final Set<String> myEndpoints = new HashSet<String>(); + + private FindIntervalVisitor(@NotNull DAG<String> dag, @NotNull Collection<String> uninteresting) { + myDag = dag; + myUninterestingRevisions = uninteresting; + } + + @Override + public boolean discover(@NotNull String revision) { + markUninteresting(); + if (!isInteresting(revision)) { + addEndpoint(revision); + return false; + } + return true; + } + + private void markUninteresting() { + if (!myVisitedUninterestingRevisions.isEmpty()) + return; + for (String rev : myUninterestingRevisions) { + myDag.breadthFirstSearch(rev, new MarkUninterestingRevisions()); + } + } + + private boolean isInteresting(@NotNull String revision) { + return !myVisitedUninterestingRevisions.contains(revision); + } + + private void addEndpoint(@NotNull String revision) { + myEndpoints.add(revision); + } + + @NotNull + public Set<String> getEndpoints() { + return myEndpoints; + } + + private class MarkUninterestingRevisions extends BFSVisitorAdapter<String> { + @Override + public boolean discover(@NotNull String node) { + if (myVisitedUninterestingRevisions.contains(node)) + return false; + myVisitedUninterestingRevisions.add(node); + return true; + } + } + } +}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/RepoFactory.java Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/RepoFactory.java Wed Mar 06 15:49:37 2013 +0400 @@ -21,6 +21,7 @@ private final CommandSettingsFactory myCommandSettingsFactory; private File myLogTemplate; private File myLogNoFilesTemplate; + private File myDagTemplate; public RepoFactory(@NotNull ServerPluginConfig config, @NotNull CommandSettingsFactory commandSettingsFactory) throws IOException { @@ -28,13 +29,15 @@ myCommandSettingsFactory = commandSettingsFactory; myLogTemplate = createLogTemplate(); myLogNoFilesTemplate = createLogNoFilesTemplate(); + myDagTemplate = createDagTemplate(); } @NotNull public ServerHgRepo create(@NotNull File workingDir, @NotNull String hgPath, @NotNull AuthSettings authSettings) throws VcsException { - return new ServerHgRepo(myCommandSettingsFactory, myConfig, workingDir, hgPath, authSettings).withLogTemplates(getTemplate(), getLogNoFilesTemplate()); + return new ServerHgRepo(myCommandSettingsFactory, myConfig, workingDir, hgPath, authSettings) + .withLogTemplates(getTemplate(), getLogNoFilesTemplate(), getDagTemplate()); } public void dispose() { @@ -63,15 +66,32 @@ } } + private File getDagTemplate() throws VcsException { + if (myDagTemplate.isFile() && myDagTemplate.exists()) + return myDagTemplate; + try { + myDagTemplate = createLogNoFilesTemplate(); + return myDagTemplate; + } catch (IOException e) { + throw new VcsException("Cannot create mercurial log template", e); + } + } + private File createLogTemplate() throws IOException { - File template = createTempFile("teamcity", "hg.log.template"); - FileUtil.copyResource(RepoFactory.class, "/buildServerResources/log.template", template); - return template; + return createTmpTemplateFile("hg.log.template", "/buildServerResources/log.template"); } private File createLogNoFilesTemplate() throws IOException { - File template = createTempFile("teamcity", "hg.short.log.template"); - FileUtil.copyResource(RepoFactory.class, "/buildServerResources/log.no.files.template", template); + return createTmpTemplateFile("hg.short.log.template", "/buildServerResources/log.no.files.template"); + } + + private File createDagTemplate() throws IOException { + return createTmpTemplateFile("hg.dag.template", "/buildServerResources/dag.template"); + } + + private File createTmpTemplateFile(@NotNull String tmpFileSuffix, @NotNull String resourcePath) throws IOException { + File template = createTempFile("teamcity", tmpFileSuffix); + FileUtil.copyResource(RepoFactory.class, resourcePath, template); return template; } }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgRepo.java Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgRepo.java Wed Mar 06 15:49:37 2013 +0400 @@ -1,12 +1,16 @@ 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 jetbrains.buildServer.vcs.VcsRoot; import jetbrains.buildServer.vcs.VcsRootInstance; import org.jetbrains.annotations.NotNull; import java.io.File; +import java.util.List; /** * @author dmitry.neverov @@ -18,6 +22,7 @@ private final ServerPluginConfig myConfig; private File myLogTemplate; private File myLogNoFilesTemplate; + private File myDagTemplate; public ServerHgRepo(@NotNull CommandSettingsFactory commandSettingsFactory, @NotNull ServerPluginConfig config, @@ -29,9 +34,12 @@ myConfig = config; } - public ServerHgRepo withLogTemplates(@NotNull File logTemplate, @NotNull File logNoFilesTemplate) { + public ServerHgRepo withLogTemplates(@NotNull File logTemplate, + @NotNull File logNoFilesTemplate, + @NotNull File dagTemplate) { myLogTemplate = logTemplate; myLogNoFilesTemplate = logNoFilesTemplate; + myDagTemplate = dagTemplate; return this; } @@ -73,4 +81,15 @@ long parentId = ((VcsRootInstance) root).getParentId(); return myConfig.getRevsetParentRootIds().contains(parentId); } + + public boolean supportRevsets() throws VcsException { + HgVersion hgVersion = getHgVersion(); + return hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION); + } + + @NotNull + public DAG<String> loadDag() throws VcsException { + List<Pair<String, String>> edges = new LoadDagCommand(myCommandSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings, myDagTemplate).call(); + return DAGs.createFromEdges(edges); + } }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesCommand.java Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesCommand.java Wed Mar 06 15:49:37 2013 +0400 @@ -10,16 +10,16 @@ */ public abstract class CollectChangesCommand { - protected String myFromRevision; + protected List<String> myFromRevisions; protected String myToRevision; @NotNull - public abstract List<ChangeSet> call(@NotNull String fromCommit, @NotNull String toCommit) throws VcsException; + public abstract List<ChangeSet> call(@NotNull List<String> fromCommits, @NotNull String toCommit) throws VcsException; public abstract List<ChangeSet> call() throws VcsException; - public CollectChangesCommand fromRevision(@NotNull String fromRevision) { - myFromRevision = fromRevision; + public CollectChangesCommand fromRevision(@NotNull List<String> fromRevisions) { + myFromRevisions = fromRevisions; return this; }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesNoRevsets.java Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesNoRevsets.java Wed Mar 06 15:49:37 2013 +0400 @@ -26,7 +26,13 @@ @Override public List<ChangeSet> call() throws VcsException { - return call(myFromRevision, myToRevision); + return call(myFromRevisions.get(0), myToRevision); + } + + @NotNull + @Override + public List<ChangeSet> call(@NotNull List<String> fromCommits, @NotNull String toCommit) throws VcsException { + return call(fromCommits.get(0), toCommit); } @NotNull
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesWithRevsets.java Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesWithRevsets.java Wed Mar 06 15:49:37 2013 +0400 @@ -19,14 +19,20 @@ @Override public List<ChangeSet> call() throws VcsException { - return call(myFromRevision, myToRevision); + return call(myFromRevisions, myToRevision); } @NotNull - public List<ChangeSet> call(@NotNull final String fromCommit, @NotNull final String toCommit) throws VcsException { + public List<ChangeSet> call(@NotNull final List<String> fromCommits, @NotNull final String toCommit) throws VcsException { + StringBuilder revsets = new StringBuilder(); + revsets.append("ancestors(").append(toCommit).append(") "); + for (String from : fromCommits) { + revsets.append(" - ancestors(").append(from).append(")"); + } + return myRepo.log() .showCommitsFromAllBranches() - .withRevsets("ancestors(" + toCommit + ") - ancestors(" + fromCommit + ")") + .withRevsets(revsets.toString()) .call(); } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVersionConstraint.java Wed Mar 06 15:49:37 2013 +0400 @@ -0,0 +1,37 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.TestCommandSettingsFactory; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.VersionCommand; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.ParseHgVersionException; +import org.jetbrains.annotations.NotNull; +import org.testng.annotations.DataProvider; + +import java.io.File; +import java.lang.reflect.Method; + +public class HgVersionConstraint { + + @DataProvider + public static Object[][] installedHgVersion(Method testMethod) throws Exception { + HgVersion actualVersion = getHgVersion(); + RequiredHgVersion constaint = testMethod.getAnnotation(RequiredHgVersion.class); + if (constaint == null) + constaint = testMethod.getDeclaringClass().getAnnotation(RequiredHgVersion.class); + if (constaint == null) + return new Object[][] { new Object[] { actualVersion }}; + HgVersion requiredMinVersion = parse(constaint.min()); + if (!actualVersion.isEqualsOrGreaterThan(requiredMinVersion)) + return new Object[0][]; + return new Object[][] { new Object[] { actualVersion }}; + } + + private static HgVersion getHgVersion() throws Exception { + VersionCommand versionCommand = new VersionCommand(new TestCommandSettingsFactory().create(), Util.getHgPath(), new File("..")); + return versionCommand.call(); + } + + private static HgVersion parse(@NotNull String version) throws ParseHgVersionException { + return HgVersion.parse("Mercurial Distributed SCM (version " + version); + } + +}
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Wed Mar 06 15:49:37 2013 +0400 @@ -15,7 +15,6 @@ */ package jetbrains.buildServer.buildTriggers.vcs.mercurial; -import com.intellij.execution.configurations.GeneralCommandLine; import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*; import jetbrains.buildServer.util.TestFor; import jetbrains.buildServer.vcs.*; @@ -35,13 +34,16 @@ import java.util.*; import static com.intellij.openapi.util.io.FileUtil.*; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.ModificationDataMatcher.modificationData; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.buildPatch; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.Util.copyRepository; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder.vcsRoot; import static jetbrains.buildServer.util.Util.map; import static jetbrains.buildServer.vcs.RepositoryStateFactory.createRepositoryState; +import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.not; @Test public class MercurialVcsSupportTest extends BaseMercurialTestCase { @@ -635,6 +637,54 @@ } + @RequiredHgVersion(min = "1.7.0") + @Test(dataProviderClass = HgVersionConstraint.class, dataProvider = "installedHgVersion") + public void should_not_report_redundant_changes_after_merge(@NotNull HgVersion _) throws Exception { + VcsRootImpl root = createVcsRoot(myRep2Path); + List<ModificationData> changes = myVcs.collectChanges(root, + createRepositoryState(map("default", "505c5b9d01e6", "personal-branch", "9ec402c74298"), "default"), + createRepositoryState(map("default", "df04faa7575a", "personal-branch", "9ec402c74298"), "default"), + CheckoutRules.DEFAULT); + assertEquals(changes.size(), 1); + } + + + @RequiredHgVersion(min = "1.7.0") + @Test(dataProviderClass = HgVersionConstraint.class, dataProvider = "installedHgVersion") + public void should_not_report_duplicate_changes(@NotNull HgVersion _) throws Exception { + VcsRootImpl root = createVcsRoot(myRep2Path); + List<ModificationData> changes = myVcs.collectChanges(root, + createRepositoryState(map("default", "505c5b9d01e6", "personal-branch", "96b78d73081d"), "default"), + createRepositoryState(map("default", "df04faa7575a", "personal-branch", "9ec402c74298"), "default"), + CheckoutRules.DEFAULT); + assertThat(changes, not(hasItem(modificationData().withVersion("dec47d2d49bf")))); + } + + + @RequiredHgVersion(min = "1.7.0") + @Test(dataProviderClass = HgVersionConstraint.class, dataProvider = "installedHgVersion") + public void should_not_report_duplicate_changes2(@NotNull HgVersion _) throws Exception { + VcsRootImpl root = createVcsRoot(myRep2Path); + List<ModificationData> changes = myVcs.collectChanges(root, + createRepositoryState(map("default", "528572bbf77b", "personal-branch", "27184c50d7ef"), "default"), + createRepositoryState(map("default", "4780519e01aa", "personal-branch", "fd50e4842211"), "default"), + CheckoutRules.DEFAULT); + assertThat(changes, not(hasItem(modificationData().withVersion("4dbb87c381be")))); + assertThat(changes.size(), is(4)); + } + + + @RequiredHgVersion(min = "1.7.0") + @Test(dataProviderClass = HgVersionConstraint.class, dataProvider = "installedHgVersion") + public void should_not_report_all_changes_in_repository_if_default_branch_is_unrelated(@NotNull HgVersion _) throws Exception { + VcsRootImpl root = createVcsRoot(myRep2Path); + List<ModificationData> changes = myVcs.collectChanges(root, + createRepositoryState(map("NULL", "1f355761350e"), "NULL"), + createRepositoryState(map("NULL", "1f355761350e", "personal-branch", "fd50e4842211"), "NULL"), + CheckoutRules.DEFAULT); + assertEquals(0, changes.size()); + } + private void assertFiles(final List<String> expectedFiles, final ModificationData modificationData) { Set<String> actualFiles = new HashSet<String>(); for (VcsChange vc: modificationData.getChanges()) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ModificationDataMatcher.java Wed Mar 06 15:49:37 2013 +0400 @@ -0,0 +1,33 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import jetbrains.buildServer.vcs.ModificationData; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeMatcher; +import org.jetbrains.annotations.NotNull; + +public class ModificationDataMatcher extends TypeSafeMatcher<ModificationData> { + + private String myVersion; + + @Override + public boolean matchesSafely(ModificationData m) { + if (myVersion != null && !myVersion.equals(m.getDisplayVersion())) + return false; + return true; + } + + public void describeTo(Description description) { + description.appendText("modification"); + if (myVersion != null) + description.appendText(" with version ").appendValue(myVersion); + } + + public static ModificationDataMatcher modificationData() { + return new ModificationDataMatcher(); + } + + public ModificationDataMatcher withVersion(@NotNull String version) { + myVersion = version; + return this; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/RequiredHgVersion.java Wed Mar 06 15:49:37 2013 +0400 @@ -0,0 +1,9 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import java.lang.annotation.ElementType; + +@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) +@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, ElementType.TYPE}) +public @interface RequiredHgVersion { + java.lang.String min() default "1.5.2"; +}
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/UnrelatedResitoriesTest.java Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/UnrelatedResitoriesTest.java Wed Mar 06 15:49:37 2013 +0400 @@ -61,7 +61,7 @@ syncRepository(); repositoryBecamesUnrelated(); String currentVersion = syncRepository(); - assertEquals(CURRENT_VERSION_OF_NEW_REPO, currentVersion); + assertEquals("4780519e01aa", currentVersion); }
--- a/mercurial-tests/testData/README Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-tests/testData/README Wed Mar 06 15:49:37 2013 +0400 @@ -24,6 +24,26 @@ rep2 history: +o 31:1f355761350e create branch NULL (NULL) tip + +o 30:4780519e01aa continue default tip +| +| o 29:fd50e4842211 continue topic (topic) +| | +o | 28:737c6f57ef84 merge topic +|\| +| o 27:2a368008e4d9 topic2 (topic) +| | +| o 26:27184c50d7ef topic1 (topic) +| | +| o 25:4dbb87c381be start topic (topic) +| | +o | 24:528572bbf77b default3 +| | +o | 23:029040d32471 default2 +| | +o | 22:32f2afc19d75 default1 +|/ o 21:a2672ee13212 Tag for test //add tag t1 on 43023ea3f13b | o 20:43023ea3f13b Merge closed personal-branch
--- a/mercurial-tests/testData/rep2/hg/store/fncache Thu Jan 10 21:15:25 2013 +0400 +++ b/mercurial-tests/testData/rep2/hg/store/fncache Wed Mar 06 15:49:37 2013 +0400 @@ -1,11 +1,12 @@ data/dir4/file4.txt.i +data/dir1/file1.txt.i data/dir3/file3.txt.i -data/dir1/file1.txt.i -data/file.txt.i data/.hgtags.i data/dir4/file42.txt.i data/dir5/file5.txt.i -data/dir2/file2.txt.i +data/file.txt.i +data/a.i data/dir4/file43.txt.i +data/dir6/file6.txt.i data/dir4/file41.txt.i -data/dir6/file6.txt.i +data/dir2/file2.txt.i