view mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesNoRevsets.java @ 757:b7deb8f24278

replace bare File with MercurialLogTemplate
author eugene.petrenko@jetbrains.com
date Tue, 25 Feb 2014 11:36:50 +0100
parents 31a1aca3305c
children 196f62e515db
line wrap: on
line source
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;

import com.intellij.openapi.util.Pair;
import jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialLogTemplate;
import jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerHgRepo;
import jetbrains.buildServer.util.graph.DAG;
import jetbrains.buildServer.util.graph.DAGIterator;
import jetbrains.buildServer.util.graph.DAGs;
import jetbrains.buildServer.vcs.VcsException;
import org.jetbrains.annotations.NotNull;

import java.util.*;

/**
 * @author dmitry.neverov
 */
public class CollectChangesNoRevsets extends CollectChangesCommand {

  private final HgVcsRoot myRoot;
  private final ServerHgRepo myRepo;
  private final MercurialLogTemplate myLogNoFilesTemplate;

  public CollectChangesNoRevsets(@NotNull HgVcsRoot root, @NotNull ServerHgRepo repo, @NotNull MercurialLogTemplate logNoFilesTemplate) {
    myRoot = root;
    myRepo = repo;
    myLogNoFilesTemplate = logNoFilesTemplate;
  }

  @Override
  public List<ChangeSet> call() throws VcsException {
    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
  public List<ChangeSet> call(@NotNull final String fromCommit, @NotNull final String toCommit) throws VcsException {
    List<ChangeSet> revisionIntervalCsets = getRevisionsBetween(fromCommit, toCommit);
    List<Pair<String, String>> revisionIntervalEdges = getEdges(revisionIntervalCsets);
    Map<String, ChangeSet> revisionIntervalMap = getChangesetMap(revisionIntervalCsets);
    DAG<String> revisionIntervalDag = DAGs.createFromEdges(revisionIntervalEdges);
    DAGIterator<String> iter = revisionIntervalDag.iterator(toCommit);
    iter.markUninteresting(fromCommit);
    List<ChangeSet> revisionIntervalReachableChangesets = new ArrayList<ChangeSet>();
    Set<String> missingParents = new HashSet<String>();
    while (iter.hasNext()) {
      String csetId = iter.next();
      ChangeSet cset = revisionIntervalMap.get(csetId);
      if (cset != null) {
        revisionIntervalReachableChangesets.add(cset);
      } else {
        missingParents.add(csetId);
      }
    }

    if (missingParents.isEmpty()) {
      Collections.reverse(revisionIntervalReachableChangesets);
      return revisionIntervalReachableChangesets;
    }

    Pair<List<ChangeSet>, Integer> commitsWithoutFiles = getCommitsWithoutFiles(fromCommit, toCommit);
    List<ChangeSet> csetsWithoutFiles = commitsWithoutFiles.first;
    Integer minRevNum = commitsWithoutFiles.second;
    if (csetsWithoutFiles.isEmpty())
      return revisionIntervalCsets;
    List<ChangeSet> commitsWithFiles = getRevisionsBetween(minRevNum, fromCommit);
    Map<String, ChangeSet> csetMap = getChangesetMap(commitsWithFiles);
    csetMap.putAll(revisionIntervalMap);
    List<ChangeSet> result = new ArrayList<ChangeSet>();
    for (ChangeSet cset : csetsWithoutFiles) {
      ChangeSet csetWithFiles = csetMap.get(cset.getId());
      if (csetWithFiles != null)
        result.add(csetWithFiles);
    }
    return result;
  }


  @NotNull
  private Pair<List<ChangeSet>, Integer> getCommitsWithoutFiles(@NotNull String fromCommit, @NotNull String toCommit) throws VcsException {
    List<ChangeSet> csets = getAllHistoryUpTo(toCommit);
    Map<String, ChangeSet> csetsMap = getChangesetMap(csets);
    if (csetsMap.containsKey(fromCommit)) {
      DAG<String> dag = DAGs.createFromEdges(getEdges(csets));
      DAGIterator<String> iter = dag.iterator(toCommit);
      iter.markUninteresting(fromCommit);
      List<ChangeSet> result = new ArrayList<ChangeSet>();
      int minRevNum = Integer.MAX_VALUE;
      while (iter.hasNext()) {
        String commit = iter.next();
        ChangeSet cset = csetsMap.get(commit);
        if (cset == null)
          continue;//commit is parent of some commit and wasn't included in the log
        int revNum = cset.getRevNumber();
        if (revNum < minRevNum)
          minRevNum = revNum;
        result.add(cset);
      }
      Collections.reverse(result);
      return Pair.create(result, minRevNum);
    } else {
      return new Pair<List<ChangeSet>, Integer>(Collections.<ChangeSet>emptyList(), Integer.MAX_VALUE);
    }
  }


  //csetId -> cset
  private Map<String, ChangeSet> getChangesetMap(@NotNull final List<ChangeSet> csets) {
    Map<String, ChangeSet> result = new HashMap<String, ChangeSet>(csets.size());
    for (ChangeSet cset : csets) {
      result.put(cset.getId(), cset);
    }
    return result;
  }


  private List<ChangeSet> getAllHistoryUpTo(@NotNull final String revision) throws VcsException {
    return myRepo.log(myRoot)
            .fromRevision(revision)
            .toRevision("0")
            .showCommitsFromAllBranches()
            .withTemplate(myLogNoFilesTemplate)
            .call();
  }


  private List<ChangeSet> getRevisionsBetween(@NotNull String fromCommit, @NotNull String toCommit) throws VcsException {
    return myRepo.log(myRoot)
            .fromRevision(toCommit)
            .toRevision(fromCommit)
            .showCommitsFromAllBranches()
            .call();
  }

  private List<ChangeSet> getRevisionsBetween(int fromRevNum, @NotNull String toCommit) throws VcsException {
    return myRepo.log(myRoot)
            .fromRevision(toCommit)
            .toRevision(String.valueOf(fromRevNum))
            .showCommitsFromAllBranches()
            .call();
  }


  private List<Pair<String, String>> getEdges(List<ChangeSet> csets) {
    List<Pair<String, String>> result = new ArrayList<Pair<String, String>>();
    for (ChangeSet cset : csets) {
      for (ChangeSetRevision parent : cset.getParents()) {
        result.add(Pair.create(cset.getId(), parent.getId()));
      }
    }
    return result;
  }
}