comparison src/org/tmatesoft/hg/repo/HgDirstate.java @ 348:a0864b2892cd

Expose errors reading mercurial control files with exception
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 24 Nov 2011 02:57:03 +0100
parents 72c6eda838a6
children aa2e589d4e84
comparison
equal deleted inserted replaced
347:8da7ade36c57 348:a0864b2892cd
18 18
19 import static org.tmatesoft.hg.core.Nodeid.NULL; 19 import static org.tmatesoft.hg.core.Nodeid.NULL;
20 20
21 import java.io.BufferedReader; 21 import java.io.BufferedReader;
22 import java.io.File; 22 import java.io.File;
23 import java.io.FileNotFoundException;
23 import java.io.FileReader; 24 import java.io.FileReader;
24 import java.io.IOException; 25 import java.io.IOException;
25 import java.util.Collections; 26 import java.util.Collections;
26 import java.util.HashMap; 27 import java.util.HashMap;
27 import java.util.LinkedHashMap; 28 import java.util.LinkedHashMap;
28 import java.util.Map; 29 import java.util.Map;
29 import java.util.TreeSet; 30 import java.util.TreeSet;
30 31
31 import org.tmatesoft.hg.core.HgBadStateException; 32 import org.tmatesoft.hg.core.HgInvalidControlFileException;
32 import org.tmatesoft.hg.core.Nodeid; 33 import org.tmatesoft.hg.core.Nodeid;
33 import org.tmatesoft.hg.internal.DataAccess; 34 import org.tmatesoft.hg.internal.DataAccess;
34 import org.tmatesoft.hg.util.Pair; 35 import org.tmatesoft.hg.util.Pair;
35 import org.tmatesoft.hg.util.Path; 36 import org.tmatesoft.hg.util.Path;
36 import org.tmatesoft.hg.util.PathPool; 37 import org.tmatesoft.hg.util.PathPool;
71 dirstateFile = dirstate; // XXX decide whether file names shall be kept local to reader (see #branches()) or passed from outside 72 dirstateFile = dirstate; // XXX decide whether file names shall be kept local to reader (see #branches()) or passed from outside
72 this.pathPool = pathPool; 73 this.pathPool = pathPool;
73 canonicalPathRewrite = canonicalPath; 74 canonicalPathRewrite = canonicalPath;
74 } 75 }
75 76
76 private void read() { 77 /*package-local*/ void read() throws HgInvalidControlFileException {
77 normal = added = removed = merged = Collections.<Path, Record>emptyMap(); 78 normal = added = removed = merged = Collections.<Path, Record>emptyMap();
78 if (canonicalPathRewrite != null) { 79 if (canonicalPathRewrite != null) {
79 canonical2dirstateName = new HashMap<Path,Path>(); 80 canonical2dirstateName = new HashMap<Path,Path>();
80 } else { 81 } else {
81 canonical2dirstateName = Collections.emptyMap(); 82 canonical2dirstateName = Collections.emptyMap();
142 } else { 143 } else {
143 // FIXME log error? 144 // FIXME log error?
144 } 145 }
145 } 146 }
146 } catch (IOException ex) { 147 } catch (IOException ex) {
147 repo.getContext().getLog().error(getClass(), ex, null); 148 throw new HgInvalidControlFileException("Dirstate read failed", ex, dirstateFile);
148 // FIXME clean dirstate?
149 } finally { 149 } finally {
150 da.done(); 150 da.done();
151 } 151 }
152 } 152 }
153 153
162 162
163 /** 163 /**
164 * @return pair of working copy parents, with {@link Nodeid#NULL} for missing values. 164 * @return pair of working copy parents, with {@link Nodeid#NULL} for missing values.
165 */ 165 */
166 public Pair<Nodeid,Nodeid> parents() { 166 public Pair<Nodeid,Nodeid> parents() {
167 if (parents == null) { 167 assert parents != null; // instance not initialized with #read()
168 parents = readParents(repo, dirstateFile);
169 }
170 return parents; 168 return parents;
171 } 169 }
172 170
173 /** 171 /**
174 * @return pair of parents, both {@link Nodeid#NULL} if dirstate is not available 172 * @return pair of parents, both {@link Nodeid#NULL} if dirstate is not available
175 */ 173 */
176 /*package-local*/ static Pair<Nodeid, Nodeid> readParents(HgRepository repo, File dirstateFile) { 174 /*package-local*/ static Pair<Nodeid, Nodeid> readParents(HgRepository repo, File dirstateFile) throws HgInvalidControlFileException {
177 // do not read whole dirstate if all we need is WC parent information 175 // do not read whole dirstate if all we need is WC parent information
178 if (dirstateFile == null || !dirstateFile.exists()) { 176 if (dirstateFile == null || !dirstateFile.exists()) {
179 return new Pair<Nodeid,Nodeid>(NULL, NULL); 177 return new Pair<Nodeid,Nodeid>(NULL, NULL);
180 } 178 }
181 DataAccess da = repo.getDataAccess().create(dirstateFile); 179 DataAccess da = repo.getDataAccess().create(dirstateFile);
183 return new Pair<Nodeid,Nodeid>(NULL, NULL); 181 return new Pair<Nodeid,Nodeid>(NULL, NULL);
184 } 182 }
185 try { 183 try {
186 return internalReadParents(da); 184 return internalReadParents(da);
187 } catch (IOException ex) { 185 } catch (IOException ex) {
188 throw new HgBadStateException(ex); // XXX in fact, our exception is not the best solution here. 186 throw new HgInvalidControlFileException("Error reading working copy parents from dirstate", ex, dirstateFile);
189 } finally { 187 } finally {
190 da.done(); 188 da.done();
191 } 189 }
192 } 190 }
193 191
194 /** 192 /**
193 * FIXME move to a better place, e.g. WorkingCopy container that tracks both dirstate and branches (and, perhaps, undo, lastcommit and other similar information)
195 * @return branch associated with the working directory 194 * @return branch associated with the working directory
196 */ 195 */
197 public String branch() { 196 public String branch() throws HgInvalidControlFileException {
198 // XXX is it really proper place for the method? 197 // XXX is it really proper place for the method?
199 if (currentBranch == null) { 198 if (currentBranch == null) {
200 currentBranch = readBranch(repo); 199 currentBranch = readBranch(repo);
201 } 200 }
202 return currentBranch; 201 return currentBranch;
204 203
205 /** 204 /**
206 * XXX is it really proper place for the method? 205 * XXX is it really proper place for the method?
207 * @return branch associated with the working directory 206 * @return branch associated with the working directory
208 */ 207 */
209 /*package-local*/ static String readBranch(HgRepository repo) { 208 /*package-local*/ static String readBranch(HgRepository repo) throws HgInvalidControlFileException {
210 String branch = HgRepository.DEFAULT_BRANCH_NAME; 209 String branch = HgRepository.DEFAULT_BRANCH_NAME;
211 File branchFile = new File(repo.getRepositoryRoot(), "branch"); 210 File branchFile = new File(repo.getRepositoryRoot(), "branch");
212 if (branchFile.exists()) { 211 if (branchFile.exists()) {
213 try { 212 try {
214 BufferedReader r = new BufferedReader(new FileReader(branchFile)); 213 BufferedReader r = new BufferedReader(new FileReader(branchFile));
216 if (b != null) { 215 if (b != null) {
217 b = b.trim().intern(); 216 b = b.trim().intern();
218 } 217 }
219 branch = b == null || b.length() == 0 ? HgRepository.DEFAULT_BRANCH_NAME : b; 218 branch = b == null || b.length() == 0 ? HgRepository.DEFAULT_BRANCH_NAME : b;
220 r.close(); 219 r.close();
220 } catch (FileNotFoundException ex) {
221 repo.getContext().getLog().debug(HgDirstate.class, ex, null); // log verbose debug, exception might be legal here
222 // IGNORE
221 } catch (IOException ex) { 223 } catch (IOException ex) {
222 repo.getContext().getLog().debug(HgDirstate.class, ex, null); // log verbose debug, exception might be legal here (i.e. FileNotFound) 224 throw new HgInvalidControlFileException("Error reading file with branch information", ex, branchFile);
223 // IGNORE
224 } 225 }
225 } 226 }
226 return branch; 227 return branch;
227 } 228 }
228 229
229 // new, modifiable collection 230 // new, modifiable collection
230 /*package-local*/ TreeSet<Path> all() { 231 /*package-local*/ TreeSet<Path> all() {
231 if (normal == null) { 232 assert normal != null;
232 read();
233 }
234 TreeSet<Path> rv = new TreeSet<Path>(); 233 TreeSet<Path> rv = new TreeSet<Path>();
235 @SuppressWarnings("unchecked") 234 @SuppressWarnings("unchecked")
236 Map<Path, Record>[] all = new Map[] { normal, added, removed, merged }; 235 Map<Path, Record>[] all = new Map[] { normal, added, removed, merged };
237 for (int i = 0; i < all.length; i++) { 236 for (int i = 0; i < all.length; i++) {
238 for (Record r : all[i].values()) { 237 for (Record r : all[i].values()) {
297 } 296 }
298 } 297 }
299 return null; 298 return null;
300 } 299 }
301 300
302
303 /*package-local*/ void dump() {
304 read();
305 @SuppressWarnings("unchecked")
306 Map<Path, Record>[] all = new Map[] { normal, added, removed, merged };
307 char[] x = new char[] {'n', 'a', 'r', 'm' };
308 for (int i = 0; i < all.length; i++) {
309 for (Record r : all[i].values()) {
310 System.out.printf("%c %3o%6d %30tc\t\t%s", x[i], r.mode, r.size, (long) r.time * 1000, r.name1);
311 if (r.name2 != null) {
312 System.out.printf(" --> %s", r.name2);
313 }
314 System.out.println();
315 }
316 System.out.println();
317 }
318 }
319
320 public void walk(Inspector inspector) { 301 public void walk(Inspector inspector) {
321 if (normal == null) { 302 assert normal != null;
322 read();
323 }
324 @SuppressWarnings("unchecked") 303 @SuppressWarnings("unchecked")
325 Map<Path, Record>[] all = new Map[] { normal, added, removed, merged }; 304 Map<Path, Record>[] all = new Map[] { normal, added, removed, merged };
326 for (int i = 0; i < all.length; i++) { 305 for (int i = 0; i < all.length; i++) {
327 EntryKind k = EntryKind.values()[i]; 306 EntryKind k = EntryKind.values()[i];
328 for (Record r : all[i].values()) { 307 for (Record r : all[i].values()) {
375 354
376 public int size() { 355 public int size() {
377 return size; 356 return size;
378 } 357 }
379 358
359 public int mode() {
360 return mode;
361 }
362
380 @Override 363 @Override
381 public Record clone() { 364 public Record clone() {
382 try { 365 try {
383 return (Record) super.clone(); 366 return (Record) super.clone();
384 } catch (CloneNotSupportedException ex) { 367 } catch (CloneNotSupportedException ex) {