Mercurial > hg4j
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 } |