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