Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/HgDirstate.java @ 284:7232b94f2ae3
HgDirstate shall operate with Path instead of String for file names. Use of Pair instead of array of unspecified length for parents.
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Sat, 03 Sep 2011 13:12:13 +0200 |
parents | 35125450c804 |
children | 8faad08c709b |
comparison
equal
deleted
inserted
replaced
283:7a8e1a305a78 | 284:7232b94f2ae3 |
---|---|
14 * the terms of a license other than GNU General Public License | 14 * the terms of a license other than GNU General Public License |
15 * contact TMate Software at support@hg4j.com | 15 * contact TMate Software at support@hg4j.com |
16 */ | 16 */ |
17 package org.tmatesoft.hg.repo; | 17 package org.tmatesoft.hg.repo; |
18 | 18 |
19 import static org.tmatesoft.hg.core.Nodeid.NULL; | |
20 | |
19 import java.io.BufferedReader; | 21 import java.io.BufferedReader; |
20 import java.io.File; | 22 import java.io.File; |
21 import java.io.FileReader; | 23 import java.io.FileReader; |
22 import java.io.IOException; | 24 import java.io.IOException; |
23 import java.util.Collections; | 25 import java.util.Collections; |
26 import java.util.TreeSet; | 28 import java.util.TreeSet; |
27 | 29 |
28 import org.tmatesoft.hg.core.HgBadStateException; | 30 import org.tmatesoft.hg.core.HgBadStateException; |
29 import org.tmatesoft.hg.core.Nodeid; | 31 import org.tmatesoft.hg.core.Nodeid; |
30 import org.tmatesoft.hg.internal.DataAccess; | 32 import org.tmatesoft.hg.internal.DataAccess; |
33 import org.tmatesoft.hg.util.Pair; | |
31 import org.tmatesoft.hg.util.Path; | 34 import org.tmatesoft.hg.util.Path; |
35 import org.tmatesoft.hg.util.PathPool; | |
32 | 36 |
33 | 37 |
34 /** | 38 /** |
35 * @see http://mercurial.selenic.com/wiki/DirState | 39 * @see http://mercurial.selenic.com/wiki/DirState |
36 * @see http://mercurial.selenic.com/wiki/FileFormats#dirstate | 40 * @see http://mercurial.selenic.com/wiki/FileFormats#dirstate |
40 */ | 44 */ |
41 class HgDirstate /* XXX RepoChangeListener */{ | 45 class HgDirstate /* XXX RepoChangeListener */{ |
42 | 46 |
43 private final HgRepository repo; | 47 private final HgRepository repo; |
44 private final File dirstateFile; | 48 private final File dirstateFile; |
45 // deliberate String, not Path as it seems useless to keep Path here | 49 private final PathPool pathPool; |
46 private Map<String, Record> normal; | 50 private Map<Path, Record> normal; |
47 private Map<String, Record> added; | 51 private Map<Path, Record> added; |
48 private Map<String, Record> removed; | 52 private Map<Path, Record> removed; |
49 private Map<String, Record> merged; | 53 private Map<Path, Record> merged; |
50 private Nodeid p1, p2; | 54 private Pair<Nodeid, Nodeid> parents; |
51 private String currentBranch; | 55 private String currentBranch; |
52 | 56 |
53 public HgDirstate(HgRepository hgRepo, File dirstate) { | 57 public HgDirstate(HgRepository hgRepo, File dirstate, PathPool pathPool) { |
54 repo = hgRepo; | 58 repo = hgRepo; |
55 dirstateFile = dirstate; // XXX decide whether file names shall be kept local to reader (see #branches()) or passed from outside | 59 dirstateFile = dirstate; // XXX decide whether file names shall be kept local to reader (see #branches()) or passed from outside |
60 this.pathPool = pathPool; | |
56 } | 61 } |
57 | 62 |
58 private void read() { | 63 private void read() { |
59 normal = added = removed = merged = Collections.<String, Record>emptyMap(); | 64 normal = added = removed = merged = Collections.<Path, Record>emptyMap(); |
60 if (dirstateFile == null || !dirstateFile.exists()) { | 65 if (dirstateFile == null || !dirstateFile.exists()) { |
61 return; | 66 return; |
62 } | 67 } |
63 DataAccess da = repo.getDataAccess().create(dirstateFile); | 68 DataAccess da = repo.getDataAccess().create(dirstateFile); |
64 if (da.isEmpty()) { | 69 if (da.isEmpty()) { |
65 return; | 70 return; |
66 } | 71 } |
67 // not sure linked is really needed here, just for ease of debug | 72 // not sure linked is really needed here, just for ease of debug |
68 normal = new LinkedHashMap<String, Record>(); | 73 normal = new LinkedHashMap<Path, Record>(); |
69 added = new LinkedHashMap<String, Record>(); | 74 added = new LinkedHashMap<Path, Record>(); |
70 removed = new LinkedHashMap<String, Record>(); | 75 removed = new LinkedHashMap<Path, Record>(); |
71 merged = new LinkedHashMap<String, Record>(); | 76 merged = new LinkedHashMap<Path, Record>(); |
72 try { | 77 try { |
73 byte[] parents = new byte[40]; | 78 parents = internalReadParents(da); |
74 da.readBytes(parents, 0, 40); | |
75 p1 = Nodeid.fromBinary(parents, 0); | |
76 p2 = Nodeid.fromBinary(parents, 20); | |
77 parents = null; | |
78 // hg init; hg up produces an empty repository where dirstate has parents (40 bytes) only | 79 // hg init; hg up produces an empty repository where dirstate has parents (40 bytes) only |
79 while (!da.isEmpty()) { | 80 while (!da.isEmpty()) { |
80 final byte state = da.readByte(); | 81 final byte state = da.readByte(); |
81 final int fmode = da.readInt(); | 82 final int fmode = da.readInt(); |
82 final int size = da.readInt(); | 83 final int size = da.readInt(); |
93 } | 94 } |
94 } | 95 } |
95 if (fn1 == null) { | 96 if (fn1 == null) { |
96 fn1 = new String(name); | 97 fn1 = new String(name); |
97 } | 98 } |
98 Record r = new Record(fmode, size, time, fn1, fn2); | 99 Record r = new Record(fmode, size, time, pathPool.path(fn1), fn2 == null ? null : pathPool.path(fn2)); |
99 if (state == 'n') { | 100 if (state == 'n') { |
100 normal.put(r.name1, r); | 101 normal.put(r.name1, r); |
101 } else if (state == 'a') { | 102 } else if (state == 'a') { |
102 added.put(r.name1, r); | 103 added.put(r.name1, r); |
103 } else if (state == 'r') { | 104 } else if (state == 'r') { |
113 } finally { | 114 } finally { |
114 da.done(); | 115 da.done(); |
115 } | 116 } |
116 } | 117 } |
117 | 118 |
118 // do not read whole dirstate if all we need is WC parent information | 119 private static Pair<Nodeid, Nodeid> internalReadParents(DataAccess da) throws IOException { |
119 private void readParents() { | 120 byte[] parents = new byte[40]; |
121 da.readBytes(parents, 0, 40); | |
122 Nodeid n1 = Nodeid.fromBinary(parents, 0); | |
123 Nodeid n2 = Nodeid.fromBinary(parents, 20); | |
124 parents = null; | |
125 return new Pair<Nodeid, Nodeid>(n1, n2); | |
126 } | |
127 | |
128 /** | |
129 * @return array of length 2 with working copy parents, non null. | |
130 */ | |
131 public Pair<Nodeid,Nodeid> parents() { | |
132 if (parents == null) { | |
133 parents = readParents(repo, dirstateFile); | |
134 } | |
135 return parents; | |
136 } | |
137 | |
138 /** | |
139 * @return pair of parents, both {@link Nodeid#NULL} if dirstate is not available | |
140 */ | |
141 public static Pair<Nodeid, Nodeid> readParents(HgRepository repo, File dirstateFile) { | |
142 // do not read whole dirstate if all we need is WC parent information | |
120 if (dirstateFile == null || !dirstateFile.exists()) { | 143 if (dirstateFile == null || !dirstateFile.exists()) { |
121 return; | 144 return new Pair<Nodeid,Nodeid>(NULL, NULL); |
122 } | 145 } |
123 DataAccess da = repo.getDataAccess().create(dirstateFile); | 146 DataAccess da = repo.getDataAccess().create(dirstateFile); |
124 if (da.isEmpty()) { | 147 if (da.isEmpty()) { |
125 return; | 148 return new Pair<Nodeid,Nodeid>(NULL, NULL); |
126 } | 149 } |
127 try { | 150 try { |
128 byte[] parents = new byte[40]; | 151 return internalReadParents(da); |
129 da.readBytes(parents, 0, 40); | |
130 p1 = Nodeid.fromBinary(parents, 0); | |
131 p2 = Nodeid.fromBinary(parents, 20); | |
132 parents = null; | |
133 } catch (IOException ex) { | 152 } catch (IOException ex) { |
134 throw new HgBadStateException(ex); // XXX in fact, our exception is not the best solution here. | 153 throw new HgBadStateException(ex); // XXX in fact, our exception is not the best solution here. |
135 } finally { | 154 } finally { |
136 da.done(); | 155 da.done(); |
137 } | 156 } |
138 } | 157 } |
139 | 158 |
140 /** | 159 /** |
141 * @return array of length 2 with working copy parents, non null. | 160 * XXX is it really proper place for the method? |
142 */ | |
143 public Nodeid[] parents() { | |
144 if (p1 == null) { | |
145 readParents(); | |
146 } | |
147 Nodeid[] rv = new Nodeid[2]; | |
148 rv[0] = p1; | |
149 rv[1] = p2; | |
150 return rv; | |
151 } | |
152 | |
153 /** | |
154 * @return branch associated with the working directory | 161 * @return branch associated with the working directory |
155 */ | 162 */ |
156 public String branch() { | 163 public String branch() { |
157 if (currentBranch == null) { | 164 if (currentBranch == null) { |
158 currentBranch = HgRepository.DEFAULT_BRANCH_NAME; | 165 currentBranch = readBranch(repo); |
159 File branchFile = new File(repo.getRepositoryRoot(), "branch"); | |
160 if (branchFile.exists()) { | |
161 try { | |
162 BufferedReader r = new BufferedReader(new FileReader(branchFile)); | |
163 String b = r.readLine(); | |
164 if (b != null) { | |
165 b = b.trim().intern(); | |
166 } | |
167 currentBranch = b == null || b.length() == 0 ? HgRepository.DEFAULT_BRANCH_NAME : b; | |
168 r.close(); | |
169 } catch (IOException ex) { | |
170 ex.printStackTrace(); // XXX log verbose debug, exception might be legal here (i.e. FileNotFound) | |
171 // IGNORE | |
172 } | |
173 } | |
174 } | 166 } |
175 return currentBranch; | 167 return currentBranch; |
176 } | 168 } |
169 | |
170 /** | |
171 * XXX is it really proper place for the method? | |
172 * @return branch associated with the working directory | |
173 */ | |
174 public static String readBranch(HgRepository repo) { | |
175 String branch = HgRepository.DEFAULT_BRANCH_NAME; | |
176 File branchFile = new File(repo.getRepositoryRoot(), "branch"); | |
177 if (branchFile.exists()) { | |
178 try { | |
179 BufferedReader r = new BufferedReader(new FileReader(branchFile)); | |
180 String b = r.readLine(); | |
181 if (b != null) { | |
182 b = b.trim().intern(); | |
183 } | |
184 branch = b == null || b.length() == 0 ? HgRepository.DEFAULT_BRANCH_NAME : b; | |
185 r.close(); | |
186 } catch (IOException ex) { | |
187 ex.printStackTrace(); // XXX log verbose debug, exception might be legal here (i.e. FileNotFound) | |
188 // IGNORE | |
189 } | |
190 } | |
191 return branch; | |
192 } | |
177 | 193 |
178 // new, modifiable collection | 194 // new, modifiable collection |
179 /*package-local*/ TreeSet<String> all() { | 195 /*package-local*/ TreeSet<Path> all() { |
180 read(); | 196 read(); |
181 TreeSet<String> rv = new TreeSet<String>(); | 197 TreeSet<Path> rv = new TreeSet<Path>(); |
182 @SuppressWarnings("unchecked") | 198 @SuppressWarnings("unchecked") |
183 Map<String, Record>[] all = new Map[] { normal, added, removed, merged }; | 199 Map<Path, Record>[] all = new Map[] { normal, added, removed, merged }; |
184 for (int i = 0; i < all.length; i++) { | 200 for (int i = 0; i < all.length; i++) { |
185 for (Record r : all[i].values()) { | 201 for (Record r : all[i].values()) { |
186 rv.add(r.name1); | 202 rv.add(r.name1); |
187 } | 203 } |
188 } | 204 } |
189 return rv; | 205 return rv; |
190 } | 206 } |
191 | 207 |
192 /*package-local*/ Record checkNormal(Path fname) { | 208 /*package-local*/ Record checkNormal(Path fname) { |
193 return normal.get(fname.toString()); | 209 return normal.get(fname); |
194 } | 210 } |
195 | 211 |
196 /*package-local*/ Record checkAdded(Path fname) { | 212 /*package-local*/ Record checkAdded(Path fname) { |
197 return added.get(fname.toString()); | 213 return added.get(fname); |
198 } | 214 } |
199 /*package-local*/ Record checkRemoved(Path fname) { | 215 /*package-local*/ Record checkRemoved(Path fname) { |
200 return removed.get(fname.toString()); | |
201 } | |
202 /*package-local*/ Record checkRemoved(String fname) { | |
203 return removed.get(fname); | 216 return removed.get(fname); |
204 } | 217 } |
205 /*package-local*/ Record checkMerged(Path fname) { | 218 /*package-local*/ Record checkMerged(Path fname) { |
206 return merged.get(fname.toString()); | 219 return merged.get(fname); |
207 } | 220 } |
208 | 221 |
209 | 222 |
210 | 223 |
211 | 224 |
212 /*package-local*/ void dump() { | 225 /*package-local*/ void dump() { |
213 read(); | 226 read(); |
214 @SuppressWarnings("unchecked") | 227 @SuppressWarnings("unchecked") |
215 Map<String, Record>[] all = new Map[] { normal, added, removed, merged }; | 228 Map<Path, Record>[] all = new Map[] { normal, added, removed, merged }; |
216 char[] x = new char[] {'n', 'a', 'r', 'm' }; | 229 char[] x = new char[] {'n', 'a', 'r', 'm' }; |
217 for (int i = 0; i < all.length; i++) { | 230 for (int i = 0; i < all.length; i++) { |
218 for (Record r : all[i].values()) { | 231 for (Record r : all[i].values()) { |
219 System.out.printf("%c %3o%6d %30tc\t\t%s", x[i], r.mode, r.size, (long) r.time * 1000, r.name1); | 232 System.out.printf("%c %3o%6d %30tc\t\t%s", x[i], r.mode, r.size, (long) r.time * 1000, r.name1); |
220 if (r.name2 != null) { | 233 if (r.name2 != null) { |
230 final int mode; | 243 final int mode; |
231 // it seems Dirstate keeps local file size (i.e. that with any filters already applied). | 244 // it seems Dirstate keeps local file size (i.e. that with any filters already applied). |
232 // Thus, can't compare directly to HgDataFile.length() | 245 // Thus, can't compare directly to HgDataFile.length() |
233 final int size; | 246 final int size; |
234 final int time; | 247 final int time; |
235 final String name1; | 248 final Path name1; |
236 final String name2; | 249 final Path name2; |
237 | 250 |
238 public Record(int fmode, int fsize, int ftime, String name1, String name2) { | 251 public Record(int fmode, int fsize, int ftime, Path name1, Path name2) { |
239 mode = fmode; | 252 mode = fmode; |
240 size = fsize; | 253 size = fsize; |
241 time = ftime; | 254 time = ftime; |
242 this.name1 = name1; | 255 this.name1 = name1; |
243 this.name2 = name2; | 256 this.name2 = name2; |