Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/PhasesHelper.java @ 445:d0e5dc3cae6e smartgit3
Support for phases functionality from Mercurial 2.1
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Tue, 05 Jun 2012 20:50:06 +0200 |
parents | |
children | 9f0e6dfd417e |
comparison
equal
deleted
inserted
replaced
412:63c5a9d7ca3f | 445:d0e5dc3cae6e |
---|---|
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 | |
22 import java.io.BufferedReader; | |
23 import java.io.File; | |
24 import java.io.FileReader; | |
25 import java.io.IOException; | |
26 import java.util.Arrays; | |
27 import java.util.Collections; | |
28 import java.util.HashMap; | |
29 import java.util.HashSet; | |
30 import java.util.LinkedList; | |
31 import java.util.List; | |
32 | |
33 import org.tmatesoft.hg.core.HgChangeset; | |
34 import org.tmatesoft.hg.core.HgInvalidControlFileException; | |
35 import org.tmatesoft.hg.core.Nodeid; | |
36 import org.tmatesoft.hg.repo.HgChangelog; | |
37 import org.tmatesoft.hg.repo.HgInternals; | |
38 import org.tmatesoft.hg.repo.HgPhase; | |
39 import org.tmatesoft.hg.repo.HgRepository; | |
40 | |
41 /** | |
42 * Support to deal with phases feature fo Mercurial (as of Mercutial version 2.1) | |
43 * | |
44 * @author Artem Tikhomirov | |
45 * @author TMate Software Ltd. | |
46 */ | |
47 public final class PhasesHelper { | |
48 | |
49 private final HgRepository hgRepo; | |
50 private final HgChangelog.ParentWalker parentHelper; | |
51 private Boolean repoSupporsPhases; | |
52 private List<Nodeid> draftPhaseRoots; | |
53 private List<Nodeid> secretPhaseRoots; | |
54 | |
55 public PhasesHelper(HgRepository repo) { | |
56 this(repo, null); | |
57 } | |
58 | |
59 public PhasesHelper(HgRepository repo, HgChangelog.ParentWalker pw) { | |
60 hgRepo = repo; | |
61 parentHelper = pw; | |
62 } | |
63 | |
64 public boolean isCapableOfPhases() throws HgInvalidControlFileException { | |
65 if (null == repoSupporsPhases) { | |
66 repoSupporsPhases = readRoots(); | |
67 } | |
68 return repoSupporsPhases.booleanValue(); | |
69 } | |
70 | |
71 | |
72 public HgPhase getPhase(HgChangeset cset) throws HgInvalidControlFileException { | |
73 final Nodeid csetRev = cset.getNodeid(); | |
74 final int csetRevIndex = cset.getRevision(); | |
75 return getPhase(csetRevIndex, csetRev); | |
76 } | |
77 | |
78 public HgPhase getPhase(final int csetRevIndex, Nodeid csetRev) throws HgInvalidControlFileException { | |
79 if (!isCapableOfPhases()) { | |
80 return HgPhase.Undefined; | |
81 } | |
82 if (csetRev == null || csetRev.isNull()) { | |
83 csetRev = hgRepo.getChangelog().getRevision(csetRevIndex); | |
84 } | |
85 | |
86 for (HgPhase phase : new HgPhase[] {HgPhase.Secret, HgPhase.Draft }) { | |
87 List<Nodeid> roots = getPhaseRoots(phase); | |
88 if (roots.isEmpty()) { | |
89 continue; | |
90 } | |
91 if (roots.contains(csetRev)) { | |
92 return phase; | |
93 } | |
94 if (parentHelper != null) { | |
95 if (parentHelper.childrenOf(roots).contains(csetRev)) { | |
96 return phase; | |
97 } | |
98 } else { | |
99 // no parent helper | |
100 // search all descendants | |
101 int[] rootIndexes = toIndexes(roots); | |
102 Arrays.sort(rootIndexes); | |
103 if (rootIndexes[0] > csetRevIndex) { | |
104 // this phase started later than our changeset was added, try another phase | |
105 continue; | |
106 } | |
107 /* | |
108 * TODO descendants() method to build a BitSet with 1 at index of those that are descendants | |
109 * wrap it into a class with root nodeid to | |
110 * (a) collect only for a subset of repository, | |
111 * (b) be able to answer isDescendant(int csetRevIndex) using absolute indexing (i.e bitAt(csetRevIndex - rootRevIndex)) | |
112 */ | |
113 final HashSet<Nodeid> parents2consider = new HashSet<Nodeid>(roots); | |
114 final boolean[] result = new boolean[] { false }; | |
115 hgRepo.getChangelog().walk(rootIndexes[0], csetRevIndex, new HgChangelog.ParentInspector() { | |
116 | |
117 public void next(int revisionIndex, Nodeid revision, int parent1, int parent2, Nodeid nidParent1, Nodeid nidParent2) { | |
118 boolean descendant = false; | |
119 if (!nidParent1.isNull() && parents2consider.contains(nidParent1)) { | |
120 parents2consider.add(nidParent1); | |
121 descendant = true; | |
122 } | |
123 if (!nidParent2.isNull() && parents2consider.contains(nidParent2)) { | |
124 parents2consider.add(nidParent2); | |
125 descendant = true; | |
126 } | |
127 if (descendant && revisionIndex == csetRevIndex) { | |
128 // revision of interest descends from one of the roots | |
129 result[0] = true; | |
130 } | |
131 } | |
132 }); | |
133 if (result[0]) { | |
134 return phase; | |
135 } | |
136 } | |
137 } | |
138 return HgPhase.Public; | |
139 | |
140 } | |
141 | |
142 | |
143 private int[] toIndexes(List<Nodeid> roots) throws HgInvalidControlFileException { | |
144 int[] rv = new int[roots.size()]; | |
145 for (int i = 0; i < rv.length; i++) { | |
146 rv[i] = hgRepo.getChangelog().getRevisionIndex(roots.get(i)); | |
147 } | |
148 return rv; | |
149 } | |
150 | |
151 private Boolean readRoots() throws HgInvalidControlFileException { | |
152 // FIXME shall access phaseroots through HgRepository#repoPathHelper | |
153 File phaseroots = new File(HgInternals.getRepositoryDir(hgRepo), "store/phaseroots"); | |
154 try { | |
155 if (!phaseroots.exists()) { | |
156 return Boolean.FALSE; | |
157 } | |
158 HashMap<HgPhase, List<Nodeid>> phase2roots = new HashMap<HgPhase, List<Nodeid>>(); | |
159 BufferedReader br = new BufferedReader(new FileReader(phaseroots)); | |
160 String line; | |
161 while ((line = br.readLine()) != null) { | |
162 String[] lc = line.trim().split("\\s+"); | |
163 if (lc.length == 0) { | |
164 continue; | |
165 } | |
166 if (lc.length != 2) { | |
167 HgInternals.getContext(hgRepo).getLog().warn(getClass(), "Bad line in phaseroots:%s", line); | |
168 continue; | |
169 } | |
170 int phaseIndex = Integer.parseInt(lc[0]); | |
171 Nodeid rootRev = Nodeid.fromAscii(lc[1]); | |
172 HgPhase phase = HgPhase.parse(phaseIndex); | |
173 List<Nodeid> roots = phase2roots.get(phase); | |
174 if (roots == null) { | |
175 phase2roots.put(phase, roots = new LinkedList<Nodeid>()); | |
176 } | |
177 roots.add(rootRev); | |
178 } | |
179 draftPhaseRoots = phase2roots.containsKey(Draft) ? phase2roots.get(Draft) : Collections.<Nodeid>emptyList(); | |
180 secretPhaseRoots = phase2roots.containsKey(Secret) ? phase2roots.get(Secret) : Collections.<Nodeid>emptyList(); | |
181 } catch (IOException ex) { | |
182 throw new HgInvalidControlFileException(ex.toString(), ex, phaseroots); | |
183 } | |
184 return Boolean.TRUE; | |
185 } | |
186 | |
187 private List<Nodeid> getPhaseRoots(HgPhase phase) { | |
188 switch (phase) { | |
189 case Draft : return draftPhaseRoots; | |
190 case Secret : return secretPhaseRoots; | |
191 } | |
192 return Collections.emptyList(); | |
193 } | |
194 } |