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