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 }