tikhomirov@445: /* tikhomirov@445: * Copyright (c) 2012 TMate Software Ltd tikhomirov@445: * tikhomirov@445: * This program is free software; you can redistribute it and/or modify tikhomirov@445: * it under the terms of the GNU General Public License as published by tikhomirov@445: * the Free Software Foundation; version 2 of the License. tikhomirov@445: * tikhomirov@445: * This program is distributed in the hope that it will be useful, tikhomirov@445: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@445: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@445: * GNU General Public License for more details. tikhomirov@445: * tikhomirov@445: * For information on how to redistribute this software under tikhomirov@445: * the terms of a license other than GNU General Public License tikhomirov@445: * contact TMate Software at support@hg4j.com tikhomirov@445: */ tikhomirov@445: package org.tmatesoft.hg.internal; tikhomirov@445: tikhomirov@445: import static org.tmatesoft.hg.repo.HgPhase.Draft; tikhomirov@445: import static org.tmatesoft.hg.repo.HgPhase.Secret; tikhomirov@445: tikhomirov@445: import java.io.BufferedReader; tikhomirov@445: import java.io.File; tikhomirov@445: import java.io.FileReader; tikhomirov@445: import java.io.IOException; tikhomirov@445: import java.util.Collections; tikhomirov@445: import java.util.HashMap; tikhomirov@445: import java.util.LinkedList; tikhomirov@445: import java.util.List; tikhomirov@445: tikhomirov@445: import org.tmatesoft.hg.core.HgChangeset; tikhomirov@445: import org.tmatesoft.hg.core.HgInvalidControlFileException; tikhomirov@445: import org.tmatesoft.hg.core.Nodeid; tikhomirov@445: import org.tmatesoft.hg.repo.HgChangelog; tikhomirov@445: import org.tmatesoft.hg.repo.HgInternals; tikhomirov@445: import org.tmatesoft.hg.repo.HgPhase; tikhomirov@445: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@445: tikhomirov@445: /** tikhomirov@445: * Support to deal with phases feature fo Mercurial (as of Mercutial version 2.1) tikhomirov@445: * tikhomirov@445: * @author Artem Tikhomirov tikhomirov@445: * @author TMate Software Ltd. tikhomirov@445: */ tikhomirov@445: public final class PhasesHelper { tikhomirov@445: tikhomirov@449: private final HgRepository repo; tikhomirov@445: private final HgChangelog.ParentWalker parentHelper; tikhomirov@445: private Boolean repoSupporsPhases; tikhomirov@445: private List draftPhaseRoots; tikhomirov@445: private List secretPhaseRoots; tikhomirov@449: private RevisionDescendants[][] phaseDescendants = new RevisionDescendants[HgPhase.values().length][]; tikhomirov@445: tikhomirov@449: public PhasesHelper(HgRepository hgRepo) { tikhomirov@449: this(hgRepo, null); tikhomirov@445: } tikhomirov@445: tikhomirov@449: public PhasesHelper(HgRepository hgRepo, HgChangelog.ParentWalker pw) { tikhomirov@449: repo = hgRepo; tikhomirov@445: parentHelper = pw; tikhomirov@445: } tikhomirov@445: tikhomirov@445: public boolean isCapableOfPhases() throws HgInvalidControlFileException { tikhomirov@445: if (null == repoSupporsPhases) { tikhomirov@445: repoSupporsPhases = readRoots(); tikhomirov@445: } tikhomirov@445: return repoSupporsPhases.booleanValue(); tikhomirov@445: } tikhomirov@445: tikhomirov@445: tikhomirov@445: public HgPhase getPhase(HgChangeset cset) throws HgInvalidControlFileException { tikhomirov@445: final Nodeid csetRev = cset.getNodeid(); tikhomirov@445: final int csetRevIndex = cset.getRevision(); tikhomirov@445: return getPhase(csetRevIndex, csetRev); tikhomirov@445: } tikhomirov@445: tikhomirov@445: public HgPhase getPhase(final int csetRevIndex, Nodeid csetRev) throws HgInvalidControlFileException { tikhomirov@445: if (!isCapableOfPhases()) { tikhomirov@445: return HgPhase.Undefined; tikhomirov@445: } tikhomirov@449: // csetRev is only used when parentHelper is available tikhomirov@449: if (parentHelper != null && (csetRev == null || csetRev.isNull())) { tikhomirov@449: csetRev = repo.getChangelog().getRevision(csetRevIndex); tikhomirov@445: } tikhomirov@445: tikhomirov@445: for (HgPhase phase : new HgPhase[] {HgPhase.Secret, HgPhase.Draft }) { tikhomirov@445: List roots = getPhaseRoots(phase); tikhomirov@445: if (roots.isEmpty()) { tikhomirov@445: continue; tikhomirov@445: } tikhomirov@445: if (parentHelper != null) { tikhomirov@449: if (roots.contains(csetRev)) { tikhomirov@449: return phase; tikhomirov@449: } tikhomirov@445: if (parentHelper.childrenOf(roots).contains(csetRev)) { tikhomirov@445: return phase; tikhomirov@445: } tikhomirov@445: } else { tikhomirov@445: // no parent helper tikhomirov@449: // search all descendants.RevisuionDescendats includes root as well. tikhomirov@449: for (RevisionDescendants rd : getPhaseDescendants(phase)) { tikhomirov@449: // isCandidate is to go straight to another root if changeset was added later that the current root tikhomirov@449: if (rd.isCandidate(csetRevIndex) && rd.isDescendant(csetRevIndex)) { tikhomirov@449: return phase; tikhomirov@445: } tikhomirov@445: } tikhomirov@445: } tikhomirov@445: } tikhomirov@445: return HgPhase.Public; tikhomirov@445: tikhomirov@445: } tikhomirov@445: tikhomirov@445: private Boolean readRoots() throws HgInvalidControlFileException { tikhomirov@445: // FIXME shall access phaseroots through HgRepository#repoPathHelper tikhomirov@449: File phaseroots = new File(HgInternals.getRepositoryDir(repo), "store/phaseroots"); tikhomirov@445: try { tikhomirov@445: if (!phaseroots.exists()) { tikhomirov@445: return Boolean.FALSE; tikhomirov@445: } tikhomirov@445: HashMap> phase2roots = new HashMap>(); tikhomirov@445: BufferedReader br = new BufferedReader(new FileReader(phaseroots)); tikhomirov@445: String line; tikhomirov@445: while ((line = br.readLine()) != null) { tikhomirov@445: String[] lc = line.trim().split("\\s+"); tikhomirov@445: if (lc.length == 0) { tikhomirov@445: continue; tikhomirov@445: } tikhomirov@445: if (lc.length != 2) { tikhomirov@449: HgInternals.getContext(repo).getLog().warn(getClass(), "Bad line in phaseroots:%s", line); tikhomirov@445: continue; tikhomirov@445: } tikhomirov@445: int phaseIndex = Integer.parseInt(lc[0]); tikhomirov@445: Nodeid rootRev = Nodeid.fromAscii(lc[1]); tikhomirov@445: HgPhase phase = HgPhase.parse(phaseIndex); tikhomirov@445: List roots = phase2roots.get(phase); tikhomirov@445: if (roots == null) { tikhomirov@445: phase2roots.put(phase, roots = new LinkedList()); tikhomirov@445: } tikhomirov@445: roots.add(rootRev); tikhomirov@445: } tikhomirov@445: draftPhaseRoots = phase2roots.containsKey(Draft) ? phase2roots.get(Draft) : Collections.emptyList(); tikhomirov@445: secretPhaseRoots = phase2roots.containsKey(Secret) ? phase2roots.get(Secret) : Collections.emptyList(); tikhomirov@445: } catch (IOException ex) { tikhomirov@445: throw new HgInvalidControlFileException(ex.toString(), ex, phaseroots); tikhomirov@445: } tikhomirov@445: return Boolean.TRUE; tikhomirov@445: } tikhomirov@445: tikhomirov@445: private List getPhaseRoots(HgPhase phase) { tikhomirov@445: switch (phase) { tikhomirov@445: case Draft : return draftPhaseRoots; tikhomirov@445: case Secret : return secretPhaseRoots; tikhomirov@445: } tikhomirov@445: return Collections.emptyList(); tikhomirov@445: } tikhomirov@449: tikhomirov@449: tikhomirov@449: private RevisionDescendants[] getPhaseDescendants(HgPhase phase) throws HgInvalidControlFileException { tikhomirov@447: int ordinal = phase.ordinal(); tikhomirov@449: if (phaseDescendants[ordinal] == null) { tikhomirov@449: phaseDescendants[ordinal] = buildPhaseDescendants(phase); tikhomirov@447: } tikhomirov@449: return phaseDescendants[ordinal]; tikhomirov@449: } tikhomirov@449: tikhomirov@449: private RevisionDescendants[] buildPhaseDescendants(HgPhase phase) throws HgInvalidControlFileException { tikhomirov@449: int[] roots = toIndexes(getPhaseRoots(phase)); tikhomirov@449: RevisionDescendants[] rv = new RevisionDescendants[roots.length]; tikhomirov@449: for (int i = 0; i < roots.length; i++) { tikhomirov@449: rv[i] = new RevisionDescendants(repo, roots[i]); tikhomirov@449: rv[i].build(); tikhomirov@449: } tikhomirov@449: return rv; tikhomirov@449: } tikhomirov@449: tikhomirov@449: private int[] toIndexes(List roots) throws HgInvalidControlFileException { tikhomirov@449: int[] rv = new int[roots.size()]; tikhomirov@449: for (int i = 0; i < rv.length; i++) { tikhomirov@449: rv[i] = repo.getChangelog().getRevisionIndex(roots.get(i)); tikhomirov@449: } tikhomirov@449: return rv; tikhomirov@447: } tikhomirov@445: }