Mercurial > hg > mercurial
view mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/OperationContext.java @ 564:d012388935fb Faradi-7.1.x
TW-26379 optimize changes collecting
Take revisions of all branches into account
author | Dmitry Neverov <dmitry.neverov@jetbrains.com> |
---|---|
date | Wed, 06 Mar 2013 15:49:37 +0400 |
parents | |
children | 844fc8f99c29 |
line wrap: on
line source
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; } } } }