Mercurial > jhg
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 470:31bd09da0dcf | 471:7bcfbc255f48 |
|---|---|
| 1 /* | |
| 2 * Copyright (c) 2012 TMate Software Ltd | |
| 3 * | |
| 4 * This program is free software; you can redistribute it and/or modify | |
| 5 * it under the terms of the GNU General Public License as published by | |
| 6 * the Free Software Foundation; version 2 of the License. | |
| 7 * | |
| 8 * This program is distributed in the hope that it will be useful, | |
| 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 11 * GNU General Public License for more details. | |
| 12 * | |
| 13 * For information on how to redistribute this software under | |
| 14 * the terms of a license other than GNU General Public License | |
| 15 * contact TMate Software at support@hg4j.com | |
| 16 */ | |
| 17 package org.tmatesoft.hg.internal; | |
| 18 | |
| 19 import static org.tmatesoft.hg.repo.HgPhase.Draft; | |
| 20 import static org.tmatesoft.hg.repo.HgPhase.Secret; | |
| 21 import static org.tmatesoft.hg.util.LogFacility.Severity.Info; | |
| 22 import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; | |
| 23 | |
| 24 import java.io.BufferedReader; | |
| 25 import java.io.File; | |
| 26 import java.io.FileReader; | |
| 27 import java.io.IOException; | |
| 28 import java.util.Collections; | |
| 29 import java.util.HashMap; | |
| 30 import java.util.LinkedList; | |
| 31 import java.util.List; | |
| 32 | |
| 33 import org.tmatesoft.hg.core.HgChangeset; | |
| 34 import org.tmatesoft.hg.core.Nodeid; | |
| 35 import org.tmatesoft.hg.repo.HgChangelog; | |
| 36 import org.tmatesoft.hg.repo.HgInternals; | |
| 37 import org.tmatesoft.hg.repo.HgInvalidControlFileException; | |
| 38 import org.tmatesoft.hg.repo.HgParentChildMap; | |
| 39 import org.tmatesoft.hg.repo.HgPhase; | |
| 40 import org.tmatesoft.hg.repo.HgRepository; | |
| 41 | |
| 42 /** | |
| 43 * Support to deal with phases feature fo Mercurial (as of Mercutial version 2.1) | |
| 44 * | |
| 45 * @author Artem Tikhomirov | |
| 46 * @author TMate Software Ltd. | |
| 47 */ | |
| 48 public final class PhasesHelper { | |
| 49 | |
| 50 private final HgRepository repo; | |
| 51 private final HgParentChildMap<HgChangelog> parentHelper; | |
| 52 private Boolean repoSupporsPhases; | |
| 53 private List<Nodeid> draftPhaseRoots; | |
| 54 private List<Nodeid> secretPhaseRoots; | |
| 55 private RevisionDescendants[][] phaseDescendants = new RevisionDescendants[HgPhase.values().length][]; | |
| 56 | |
| 57 public PhasesHelper(HgRepository hgRepo) { | |
| 58 this(hgRepo, null); | |
| 59 } | |
| 60 | |
| 61 public PhasesHelper(HgRepository hgRepo, HgParentChildMap<HgChangelog> pw) { | |
| 62 repo = hgRepo; | |
| 63 parentHelper = pw; | |
| 64 } | |
| 65 | |
| 66 public boolean isCapableOfPhases() throws HgInvalidControlFileException { | |
| 67 if (null == repoSupporsPhases) { | |
| 68 repoSupporsPhases = readRoots(); | |
| 69 } | |
| 70 return repoSupporsPhases.booleanValue(); | |
| 71 } | |
| 72 | |
| 73 | |
| 74 public HgPhase getPhase(HgChangeset cset) throws HgInvalidControlFileException { | |
| 75 final Nodeid csetRev = cset.getNodeid(); | |
| 76 final int csetRevIndex = cset.getRevisionIndex(); | |
| 77 return getPhase(csetRevIndex, csetRev); | |
| 78 } | |
| 79 | |
| 80 public HgPhase getPhase(final int csetRevIndex, Nodeid csetRev) throws HgInvalidControlFileException { | |
| 81 if (!isCapableOfPhases()) { | |
| 82 return HgPhase.Undefined; | |
| 83 } | |
| 84 // csetRev is only used when parentHelper is available | |
| 85 if (parentHelper != null && (csetRev == null || csetRev.isNull())) { | |
| 86 csetRev = repo.getChangelog().getRevision(csetRevIndex); | |
| 87 } | |
| 88 | |
| 89 for (HgPhase phase : new HgPhase[] {HgPhase.Secret, HgPhase.Draft }) { | |
| 90 List<Nodeid> roots = getPhaseRoots(phase); | |
| 91 if (roots.isEmpty()) { | |
| 92 continue; | |
| 93 } | |
| 94 if (parentHelper != null) { | |
| 95 if (roots.contains(csetRev)) { | |
| 96 return phase; | |
| 97 } | |
| 98 if (parentHelper.childrenOf(roots).contains(csetRev)) { | |
| 99 return phase; | |
| 100 } | |
| 101 } else { | |
| 102 // no parent helper | |
| 103 // search all descendants.RevisuionDescendats includes root as well. | |
| 104 for (RevisionDescendants rd : getPhaseDescendants(phase)) { | |
| 105 // isCandidate is to go straight to another root if changeset was added later that the current root | |
| 106 if (rd.isCandidate(csetRevIndex) && rd.isDescendant(csetRevIndex)) { | |
| 107 return phase; | |
| 108 } | |
| 109 } | |
| 110 } | |
| 111 } | |
| 112 return HgPhase.Public; | |
| 113 | |
| 114 } | |
| 115 | |
| 116 private Boolean readRoots() throws HgInvalidControlFileException { | |
| 117 // FIXME shall access phaseroots through HgRepository#repoPathHelper | |
| 118 File phaseroots = new File(HgInternals.getRepositoryDir(repo), "store/phaseroots"); | |
| 119 BufferedReader br = null; | |
| 120 try { | |
| 121 if (!phaseroots.exists()) { | |
| 122 return Boolean.FALSE; | |
| 123 } | |
| 124 HashMap<HgPhase, List<Nodeid>> phase2roots = new HashMap<HgPhase, List<Nodeid>>(); | |
| 125 br = new BufferedReader(new FileReader(phaseroots)); | |
| 126 String line; | |
| 127 while ((line = br.readLine()) != null) { | |
| 128 String[] lc = line.trim().split("\\s+"); | |
| 129 if (lc.length == 0) { | |
| 130 continue; | |
| 131 } | |
| 132 if (lc.length != 2) { | |
| 133 HgInternals.getContext(repo).getLog().dump(getClass(), Warn, "Bad line in phaseroots:%s", line); | |
| 134 continue; | |
| 135 } | |
| 136 int phaseIndex = Integer.parseInt(lc[0]); | |
| 137 Nodeid rootRev = Nodeid.fromAscii(lc[1]); | |
| 138 if (!repo.getChangelog().isKnown(rootRev)) { | |
| 139 HgInternals.getContext(repo).getLog().dump(getClass(), Warn, "Phase(%d) root node %s doesn't exist in the repository, ignored.", phaseIndex, rootRev); | |
| 140 continue; | |
| 141 } | |
| 142 HgPhase phase = HgPhase.parse(phaseIndex); | |
| 143 List<Nodeid> roots = phase2roots.get(phase); | |
| 144 if (roots == null) { | |
| 145 phase2roots.put(phase, roots = new LinkedList<Nodeid>()); | |
| 146 } | |
| 147 roots.add(rootRev); | |
| 148 } | |
| 149 draftPhaseRoots = phase2roots.containsKey(Draft) ? phase2roots.get(Draft) : Collections.<Nodeid>emptyList(); | |
| 150 secretPhaseRoots = phase2roots.containsKey(Secret) ? phase2roots.get(Secret) : Collections.<Nodeid>emptyList(); | |
| 151 } catch (IOException ex) { | |
| 152 throw new HgInvalidControlFileException(ex.toString(), ex, phaseroots); | |
| 153 } finally { | |
| 154 if (br != null) { | |
| 155 try { | |
| 156 br.close(); | |
| 157 } catch (IOException ex) { | |
| 158 HgInternals.getContext(repo).getLog().dump(getClass(), Info, ex, null); | |
| 159 // ignore the exception otherwise | |
| 160 } | |
| 161 } | |
| 162 } | |
| 163 return Boolean.TRUE; | |
| 164 } | |
| 165 | |
| 166 private List<Nodeid> getPhaseRoots(HgPhase phase) { | |
| 167 switch (phase) { | |
| 168 case Draft : return draftPhaseRoots; | |
| 169 case Secret : return secretPhaseRoots; | |
| 170 } | |
| 171 return Collections.emptyList(); | |
| 172 } | |
| 173 | |
| 174 | |
| 175 private RevisionDescendants[] getPhaseDescendants(HgPhase phase) throws HgInvalidControlFileException { | |
| 176 int ordinal = phase.ordinal(); | |
| 177 if (phaseDescendants[ordinal] == null) { | |
| 178 phaseDescendants[ordinal] = buildPhaseDescendants(phase); | |
| 179 } | |
| 180 return phaseDescendants[ordinal]; | |
| 181 } | |
| 182 | |
| 183 private RevisionDescendants[] buildPhaseDescendants(HgPhase phase) throws HgInvalidControlFileException { | |
| 184 int[] roots = toIndexes(getPhaseRoots(phase)); | |
| 185 RevisionDescendants[] rv = new RevisionDescendants[roots.length]; | |
| 186 for (int i = 0; i < roots.length; i++) { | |
| 187 rv[i] = new RevisionDescendants(repo, roots[i]); | |
| 188 rv[i].build(); | |
| 189 } | |
| 190 return rv; | |
| 191 } | |
| 192 | |
| 193 private int[] toIndexes(List<Nodeid> roots) throws HgInvalidControlFileException { | |
| 194 int[] rv = new int[roots.size()]; | |
| 195 for (int i = 0; i < rv.length; i++) { | |
| 196 rv[i] = repo.getChangelog().getRevisionIndex(roots.get(i)); | |
| 197 } | |
| 198 return rv; | |
| 199 } | |
| 200 } |
