Mercurial > jhg
comparison src/org/tmatesoft/hg/internal/PhasesHelper.java @ 449:5787e912f60e smartgit3
Speed up changeset phase detection when no parent cache is avalable
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Thu, 07 Jun 2012 16:01:09 +0200 |
| parents | 2e402c12ebc6 |
| children | 39fe00407937 |
comparison
equal
deleted
inserted
replaced
| 448:2e402c12ebc6 | 449:5787e912f60e |
|---|---|
| 16 */ | 16 */ |
| 17 package org.tmatesoft.hg.internal; | 17 package org.tmatesoft.hg.internal; |
| 18 | 18 |
| 19 import static org.tmatesoft.hg.repo.HgPhase.Draft; | 19 import static org.tmatesoft.hg.repo.HgPhase.Draft; |
| 20 import static org.tmatesoft.hg.repo.HgPhase.Secret; | 20 import static org.tmatesoft.hg.repo.HgPhase.Secret; |
| 21 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; | |
| 22 | 21 |
| 23 import java.io.BufferedReader; | 22 import java.io.BufferedReader; |
| 24 import java.io.File; | 23 import java.io.File; |
| 25 import java.io.FileReader; | 24 import java.io.FileReader; |
| 26 import java.io.IOException; | 25 import java.io.IOException; |
| 27 import java.util.Arrays; | |
| 28 import java.util.Collections; | 26 import java.util.Collections; |
| 29 import java.util.HashMap; | 27 import java.util.HashMap; |
| 30 import java.util.HashSet; | |
| 31 import java.util.LinkedList; | 28 import java.util.LinkedList; |
| 32 import java.util.List; | 29 import java.util.List; |
| 33 | 30 |
| 34 import org.tmatesoft.hg.core.HgChangeset; | 31 import org.tmatesoft.hg.core.HgChangeset; |
| 35 import org.tmatesoft.hg.core.HgInvalidControlFileException; | 32 import org.tmatesoft.hg.core.HgInvalidControlFileException; |
| 45 * @author Artem Tikhomirov | 42 * @author Artem Tikhomirov |
| 46 * @author TMate Software Ltd. | 43 * @author TMate Software Ltd. |
| 47 */ | 44 */ |
| 48 public final class PhasesHelper { | 45 public final class PhasesHelper { |
| 49 | 46 |
| 50 private final HgRepository hgRepo; | 47 private final HgRepository repo; |
| 51 private final HgChangelog.ParentWalker parentHelper; | 48 private final HgChangelog.ParentWalker parentHelper; |
| 52 private Boolean repoSupporsPhases; | 49 private Boolean repoSupporsPhases; |
| 53 private List<Nodeid> draftPhaseRoots; | 50 private List<Nodeid> draftPhaseRoots; |
| 54 private List<Nodeid> secretPhaseRoots; | 51 private List<Nodeid> secretPhaseRoots; |
| 55 private int[] earliestRevIndex = new int[HgPhase.values().length]; | 52 private RevisionDescendants[][] phaseDescendants = new RevisionDescendants[HgPhase.values().length][]; |
| 56 | 53 |
| 57 public PhasesHelper(HgRepository repo) { | 54 public PhasesHelper(HgRepository hgRepo) { |
| 58 this(repo, null); | 55 this(hgRepo, null); |
| 59 } | 56 } |
| 60 | 57 |
| 61 public PhasesHelper(HgRepository repo, HgChangelog.ParentWalker pw) { | 58 public PhasesHelper(HgRepository hgRepo, HgChangelog.ParentWalker pw) { |
| 62 hgRepo = repo; | 59 repo = hgRepo; |
| 63 parentHelper = pw; | 60 parentHelper = pw; |
| 64 Arrays.fill(earliestRevIndex, BAD_REVISION); | |
| 65 } | 61 } |
| 66 | 62 |
| 67 public boolean isCapableOfPhases() throws HgInvalidControlFileException { | 63 public boolean isCapableOfPhases() throws HgInvalidControlFileException { |
| 68 if (null == repoSupporsPhases) { | 64 if (null == repoSupporsPhases) { |
| 69 repoSupporsPhases = readRoots(); | 65 repoSupporsPhases = readRoots(); |
| 80 | 76 |
| 81 public HgPhase getPhase(final int csetRevIndex, Nodeid csetRev) throws HgInvalidControlFileException { | 77 public HgPhase getPhase(final int csetRevIndex, Nodeid csetRev) throws HgInvalidControlFileException { |
| 82 if (!isCapableOfPhases()) { | 78 if (!isCapableOfPhases()) { |
| 83 return HgPhase.Undefined; | 79 return HgPhase.Undefined; |
| 84 } | 80 } |
| 85 if (csetRev == null || csetRev.isNull()) { | 81 // csetRev is only used when parentHelper is available |
| 86 csetRev = hgRepo.getChangelog().getRevision(csetRevIndex); | 82 if (parentHelper != null && (csetRev == null || csetRev.isNull())) { |
| 83 csetRev = repo.getChangelog().getRevision(csetRevIndex); | |
| 87 } | 84 } |
| 88 | 85 |
| 89 for (HgPhase phase : new HgPhase[] {HgPhase.Secret, HgPhase.Draft }) { | 86 for (HgPhase phase : new HgPhase[] {HgPhase.Secret, HgPhase.Draft }) { |
| 90 List<Nodeid> roots = getPhaseRoots(phase); | 87 List<Nodeid> roots = getPhaseRoots(phase); |
| 91 if (roots.isEmpty()) { | 88 if (roots.isEmpty()) { |
| 92 continue; | 89 continue; |
| 93 } | 90 } |
| 94 if (roots.contains(csetRev)) { | |
| 95 return phase; | |
| 96 } | |
| 97 if (parentHelper != null) { | 91 if (parentHelper != null) { |
| 92 if (roots.contains(csetRev)) { | |
| 93 return phase; | |
| 94 } | |
| 98 if (parentHelper.childrenOf(roots).contains(csetRev)) { | 95 if (parentHelper.childrenOf(roots).contains(csetRev)) { |
| 99 return phase; | 96 return phase; |
| 100 } | 97 } |
| 101 } else { | 98 } else { |
| 102 // no parent helper | 99 // no parent helper |
| 103 // search all descendants | 100 // search all descendants.RevisuionDescendats includes root as well. |
| 104 int earliestRootRevIndex = getEarliestPhaseRevision(phase); | 101 for (RevisionDescendants rd : getPhaseDescendants(phase)) { |
| 105 if (earliestRootRevIndex > csetRevIndex) { | 102 // isCandidate is to go straight to another root if changeset was added later that the current root |
| 106 // this phase started later than our changeset was added, try another phase | 103 if (rd.isCandidate(csetRevIndex) && rd.isDescendant(csetRevIndex)) { |
| 107 continue; | 104 return phase; |
| 108 } | |
| 109 /* | |
| 110 * TODO descendants() method to build a BitSet with 1 at index of those that are descendants | |
| 111 * wrap it into a class with root nodeid to | |
| 112 * (a) collect only for a subset of repository, | |
| 113 * (b) be able to answer isDescendant(int csetRevIndex) using absolute indexing (i.e bitAt(csetRevIndex - rootRevIndex)) | |
| 114 */ | |
| 115 final HashSet<Nodeid> parents2consider = new HashSet<Nodeid>(roots); | |
| 116 final boolean[] result = new boolean[] { false }; | |
| 117 hgRepo.getChangelog().walk(earliestRootRevIndex, csetRevIndex, new HgChangelog.ParentInspector() { | |
| 118 | |
| 119 public void next(int revisionIndex, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2) { | |
| 120 boolean descendant = false; | |
| 121 if (!nidParent1.isNull() && parents2consider.contains(nidParent1)) { | |
| 122 parents2consider.add(revision); | |
| 123 descendant = true; | |
| 124 } | |
| 125 if (!nidParent2.isNull() && parents2consider.contains(nidParent2)) { | |
| 126 parents2consider.add(revision); | |
| 127 descendant = true; | |
| 128 } | |
| 129 if (descendant && revisionIndex == csetRevIndex) { | |
| 130 // revision of interest descends from one of the roots | |
| 131 result[0] = true; | |
| 132 } | |
| 133 } | 105 } |
| 134 }); | |
| 135 if (result[0]) { | |
| 136 return phase; | |
| 137 } | 106 } |
| 138 } | 107 } |
| 139 } | 108 } |
| 140 return HgPhase.Public; | 109 return HgPhase.Public; |
| 141 | 110 |
| 142 } | 111 } |
| 143 | 112 |
| 144 | |
| 145 private int[] toIndexes(List<Nodeid> roots) throws HgInvalidControlFileException { | |
| 146 int[] rv = new int[roots.size()]; | |
| 147 for (int i = 0; i < rv.length; i++) { | |
| 148 rv[i] = hgRepo.getChangelog().getRevisionIndex(roots.get(i)); | |
| 149 } | |
| 150 return rv; | |
| 151 } | |
| 152 | |
| 153 private Boolean readRoots() throws HgInvalidControlFileException { | 113 private Boolean readRoots() throws HgInvalidControlFileException { |
| 154 // FIXME shall access phaseroots through HgRepository#repoPathHelper | 114 // FIXME shall access phaseroots through HgRepository#repoPathHelper |
| 155 File phaseroots = new File(HgInternals.getRepositoryDir(hgRepo), "store/phaseroots"); | 115 File phaseroots = new File(HgInternals.getRepositoryDir(repo), "store/phaseroots"); |
| 156 try { | 116 try { |
| 157 if (!phaseroots.exists()) { | 117 if (!phaseroots.exists()) { |
| 158 return Boolean.FALSE; | 118 return Boolean.FALSE; |
| 159 } | 119 } |
| 160 HashMap<HgPhase, List<Nodeid>> phase2roots = new HashMap<HgPhase, List<Nodeid>>(); | 120 HashMap<HgPhase, List<Nodeid>> phase2roots = new HashMap<HgPhase, List<Nodeid>>(); |
| 164 String[] lc = line.trim().split("\\s+"); | 124 String[] lc = line.trim().split("\\s+"); |
| 165 if (lc.length == 0) { | 125 if (lc.length == 0) { |
| 166 continue; | 126 continue; |
| 167 } | 127 } |
| 168 if (lc.length != 2) { | 128 if (lc.length != 2) { |
| 169 HgInternals.getContext(hgRepo).getLog().warn(getClass(), "Bad line in phaseroots:%s", line); | 129 HgInternals.getContext(repo).getLog().warn(getClass(), "Bad line in phaseroots:%s", line); |
| 170 continue; | 130 continue; |
| 171 } | 131 } |
| 172 int phaseIndex = Integer.parseInt(lc[0]); | 132 int phaseIndex = Integer.parseInt(lc[0]); |
| 173 Nodeid rootRev = Nodeid.fromAscii(lc[1]); | 133 Nodeid rootRev = Nodeid.fromAscii(lc[1]); |
| 174 HgPhase phase = HgPhase.parse(phaseIndex); | 134 HgPhase phase = HgPhase.parse(phaseIndex); |
| 191 case Draft : return draftPhaseRoots; | 151 case Draft : return draftPhaseRoots; |
| 192 case Secret : return secretPhaseRoots; | 152 case Secret : return secretPhaseRoots; |
| 193 } | 153 } |
| 194 return Collections.emptyList(); | 154 return Collections.emptyList(); |
| 195 } | 155 } |
| 156 | |
| 157 | |
| 158 private RevisionDescendants[] getPhaseDescendants(HgPhase phase) throws HgInvalidControlFileException { | |
| 159 int ordinal = phase.ordinal(); | |
| 160 if (phaseDescendants[ordinal] == null) { | |
| 161 phaseDescendants[ordinal] = buildPhaseDescendants(phase); | |
| 162 } | |
| 163 return phaseDescendants[ordinal]; | |
| 164 } | |
| 165 | |
| 166 private RevisionDescendants[] buildPhaseDescendants(HgPhase phase) throws HgInvalidControlFileException { | |
| 167 int[] roots = toIndexes(getPhaseRoots(phase)); | |
| 168 RevisionDescendants[] rv = new RevisionDescendants[roots.length]; | |
| 169 for (int i = 0; i < roots.length; i++) { | |
| 170 rv[i] = new RevisionDescendants(repo, roots[i]); | |
| 171 rv[i].build(); | |
| 172 } | |
| 173 return rv; | |
| 174 } | |
| 196 | 175 |
| 197 private int getEarliestPhaseRevision(HgPhase phase) throws HgInvalidControlFileException { | 176 private int[] toIndexes(List<Nodeid> roots) throws HgInvalidControlFileException { |
| 198 int ordinal = phase.ordinal(); | 177 int[] rv = new int[roots.size()]; |
| 199 if (earliestRevIndex[ordinal] == BAD_REVISION) { | 178 for (int i = 0; i < rv.length; i++) { |
| 200 int[] rootIndexes = toIndexes(getPhaseRoots(phase)); | 179 rv[i] = repo.getChangelog().getRevisionIndex(roots.get(i)); |
| 201 Arrays.sort(rootIndexes); | |
| 202 // instead of MAX_VALUE may use clog.getLastRevision() + 1 | |
| 203 earliestRevIndex[ordinal] = rootIndexes.length == 0 ? Integer.MAX_VALUE : rootIndexes[0]; | |
| 204 } | 180 } |
| 205 return earliestRevIndex[ordinal]; | 181 return rv; |
| 206 } | 182 } |
| 207 } | 183 } |
