Mercurial > hg4j
diff src/org/tmatesoft/hg/internal/PhasesHelper.java @ 471:7bcfbc255f48
Merge changes from smartgit3 branch into 1.1 stream
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Wed, 11 Jul 2012 20:40:47 +0200 |
parents | 39fe00407937 |
children | 09f2d38ecf26 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/PhasesHelper.java Wed Jul 11 20:40:47 2012 +0200 @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2012 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.internal; + +import static org.tmatesoft.hg.repo.HgPhase.Draft; +import static org.tmatesoft.hg.repo.HgPhase.Secret; +import static org.tmatesoft.hg.util.LogFacility.Severity.Info; +import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import org.tmatesoft.hg.core.HgChangeset; +import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.repo.HgChangelog; +import org.tmatesoft.hg.repo.HgInternals; +import org.tmatesoft.hg.repo.HgInvalidControlFileException; +import org.tmatesoft.hg.repo.HgParentChildMap; +import org.tmatesoft.hg.repo.HgPhase; +import org.tmatesoft.hg.repo.HgRepository; + +/** + * Support to deal with phases feature fo Mercurial (as of Mercutial version 2.1) + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public final class PhasesHelper { + + private final HgRepository repo; + private final HgParentChildMap<HgChangelog> parentHelper; + private Boolean repoSupporsPhases; + private List<Nodeid> draftPhaseRoots; + private List<Nodeid> secretPhaseRoots; + private RevisionDescendants[][] phaseDescendants = new RevisionDescendants[HgPhase.values().length][]; + + public PhasesHelper(HgRepository hgRepo) { + this(hgRepo, null); + } + + public PhasesHelper(HgRepository hgRepo, HgParentChildMap<HgChangelog> pw) { + repo = hgRepo; + parentHelper = pw; + } + + public boolean isCapableOfPhases() throws HgInvalidControlFileException { + if (null == repoSupporsPhases) { + repoSupporsPhases = readRoots(); + } + return repoSupporsPhases.booleanValue(); + } + + + public HgPhase getPhase(HgChangeset cset) throws HgInvalidControlFileException { + final Nodeid csetRev = cset.getNodeid(); + final int csetRevIndex = cset.getRevisionIndex(); + return getPhase(csetRevIndex, csetRev); + } + + public HgPhase getPhase(final int csetRevIndex, Nodeid csetRev) throws HgInvalidControlFileException { + if (!isCapableOfPhases()) { + return HgPhase.Undefined; + } + // csetRev is only used when parentHelper is available + if (parentHelper != null && (csetRev == null || csetRev.isNull())) { + csetRev = repo.getChangelog().getRevision(csetRevIndex); + } + + for (HgPhase phase : new HgPhase[] {HgPhase.Secret, HgPhase.Draft }) { + List<Nodeid> roots = getPhaseRoots(phase); + if (roots.isEmpty()) { + continue; + } + if (parentHelper != null) { + if (roots.contains(csetRev)) { + return phase; + } + if (parentHelper.childrenOf(roots).contains(csetRev)) { + return phase; + } + } else { + // no parent helper + // search all descendants.RevisuionDescendats includes root as well. + for (RevisionDescendants rd : getPhaseDescendants(phase)) { + // isCandidate is to go straight to another root if changeset was added later that the current root + if (rd.isCandidate(csetRevIndex) && rd.isDescendant(csetRevIndex)) { + return phase; + } + } + } + } + return HgPhase.Public; + + } + + private Boolean readRoots() throws HgInvalidControlFileException { + // FIXME shall access phaseroots through HgRepository#repoPathHelper + File phaseroots = new File(HgInternals.getRepositoryDir(repo), "store/phaseroots"); + BufferedReader br = null; + try { + if (!phaseroots.exists()) { + return Boolean.FALSE; + } + HashMap<HgPhase, List<Nodeid>> phase2roots = new HashMap<HgPhase, List<Nodeid>>(); + br = new BufferedReader(new FileReader(phaseroots)); + String line; + while ((line = br.readLine()) != null) { + String[] lc = line.trim().split("\\s+"); + if (lc.length == 0) { + continue; + } + if (lc.length != 2) { + HgInternals.getContext(repo).getLog().dump(getClass(), Warn, "Bad line in phaseroots:%s", line); + continue; + } + int phaseIndex = Integer.parseInt(lc[0]); + Nodeid rootRev = Nodeid.fromAscii(lc[1]); + if (!repo.getChangelog().isKnown(rootRev)) { + HgInternals.getContext(repo).getLog().dump(getClass(), Warn, "Phase(%d) root node %s doesn't exist in the repository, ignored.", phaseIndex, rootRev); + continue; + } + HgPhase phase = HgPhase.parse(phaseIndex); + List<Nodeid> roots = phase2roots.get(phase); + if (roots == null) { + phase2roots.put(phase, roots = new LinkedList<Nodeid>()); + } + roots.add(rootRev); + } + draftPhaseRoots = phase2roots.containsKey(Draft) ? phase2roots.get(Draft) : Collections.<Nodeid>emptyList(); + secretPhaseRoots = phase2roots.containsKey(Secret) ? phase2roots.get(Secret) : Collections.<Nodeid>emptyList(); + } catch (IOException ex) { + throw new HgInvalidControlFileException(ex.toString(), ex, phaseroots); + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException ex) { + HgInternals.getContext(repo).getLog().dump(getClass(), Info, ex, null); + // ignore the exception otherwise + } + } + } + return Boolean.TRUE; + } + + private List<Nodeid> getPhaseRoots(HgPhase phase) { + switch (phase) { + case Draft : return draftPhaseRoots; + case Secret : return secretPhaseRoots; + } + return Collections.emptyList(); + } + + + private RevisionDescendants[] getPhaseDescendants(HgPhase phase) throws HgInvalidControlFileException { + int ordinal = phase.ordinal(); + if (phaseDescendants[ordinal] == null) { + phaseDescendants[ordinal] = buildPhaseDescendants(phase); + } + return phaseDescendants[ordinal]; + } + + private RevisionDescendants[] buildPhaseDescendants(HgPhase phase) throws HgInvalidControlFileException { + int[] roots = toIndexes(getPhaseRoots(phase)); + RevisionDescendants[] rv = new RevisionDescendants[roots.length]; + for (int i = 0; i < roots.length; i++) { + rv[i] = new RevisionDescendants(repo, roots[i]); + rv[i].build(); + } + return rv; + } + + private int[] toIndexes(List<Nodeid> roots) throws HgInvalidControlFileException { + int[] rv = new int[roots.size()]; + for (int i = 0; i < rv.length; i++) { + rv[i] = repo.getChangelog().getRevisionIndex(roots.get(i)); + } + return rv; + } +}