Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgDirstate.java @ 526:2f9ed6bcefa2
Initial support for Revert command with accompanying minor refactoring
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Tue, 15 Jan 2013 17:07:19 +0100 |
parents | 0be5be8d57e9 |
children |
comparison
equal
deleted
inserted
replaced
525:0be5be8d57e9 | 526:2f9ed6bcefa2 |
---|---|
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 import static org.tmatesoft.hg.repo.HgRepositoryFiles.Dirstate; | |
21 import static org.tmatesoft.hg.util.LogFacility.Severity.Debug; | |
22 | |
23 import java.io.BufferedReader; | |
24 import java.io.File; | |
25 import java.io.FileNotFoundException; | |
26 import java.io.FileReader; | |
27 import java.io.IOException; | |
28 import java.util.Collections; | 19 import java.util.Collections; |
29 import java.util.HashMap; | 20 import java.util.HashMap; |
30 import java.util.LinkedHashMap; | 21 import java.util.LinkedHashMap; |
31 import java.util.Map; | 22 import java.util.Map; |
32 import java.util.TreeSet; | 23 import java.util.TreeSet; |
33 | 24 |
34 import org.tmatesoft.hg.core.Nodeid; | 25 import org.tmatesoft.hg.core.Nodeid; |
35 import org.tmatesoft.hg.internal.DataAccess; | 26 import org.tmatesoft.hg.internal.DirstateReader; |
36 import org.tmatesoft.hg.internal.EncodingHelper; | |
37 import org.tmatesoft.hg.internal.Internals; | 27 import org.tmatesoft.hg.internal.Internals; |
38 import org.tmatesoft.hg.util.Pair; | 28 import org.tmatesoft.hg.util.Pair; |
39 import org.tmatesoft.hg.util.Path; | 29 import org.tmatesoft.hg.util.Path; |
40 import org.tmatesoft.hg.util.PathRewrite; | 30 import org.tmatesoft.hg.util.PathRewrite; |
41 import org.tmatesoft.hg.util.LogFacility.Severity; | |
42 | 31 |
43 | 32 |
44 /** | 33 /** |
45 * @see http://mercurial.selenic.com/wiki/DirState | 34 * @see http://mercurial.selenic.com/wiki/DirState |
46 * @see http://mercurial.selenic.com/wiki/FileFormats#dirstate | 35 * @see http://mercurial.selenic.com/wiki/FileFormats#dirstate |
73 pathPool = pathSource; | 62 pathPool = pathSource; |
74 canonicalPathRewrite = canonicalPath; | 63 canonicalPathRewrite = canonicalPath; |
75 } | 64 } |
76 | 65 |
77 /*package-local*/ void read() throws HgInvalidControlFileException { | 66 /*package-local*/ void read() throws HgInvalidControlFileException { |
78 EncodingHelper encodingHelper = repo.buildFileNameEncodingHelper(); | |
79 normal = added = removed = merged = Collections.<Path, Record>emptyMap(); | 67 normal = added = removed = merged = Collections.<Path, Record>emptyMap(); |
80 parents = new Pair<Nodeid,Nodeid>(Nodeid.NULL, Nodeid.NULL); | 68 parents = new Pair<Nodeid,Nodeid>(Nodeid.NULL, Nodeid.NULL); |
81 if (canonicalPathRewrite != null) { | 69 if (canonicalPathRewrite != null) { |
82 canonical2dirstateName = new HashMap<Path,Path>(); | 70 canonical2dirstateName = new HashMap<Path,Path>(); |
83 } else { | 71 } else { |
84 canonical2dirstateName = Collections.emptyMap(); | 72 canonical2dirstateName = Collections.emptyMap(); |
85 } | 73 } |
86 File dirstateFile = getDirstateFile(repo); | 74 // not sure linked is really needed here, just for ease of debug |
87 if (dirstateFile == null || !dirstateFile.exists()) { | 75 normal = new LinkedHashMap<Path, Record>(); |
88 return; | 76 added = new LinkedHashMap<Path, Record>(); |
89 } | 77 removed = new LinkedHashMap<Path, Record>(); |
90 DataAccess da = repo.getDataAccess().create(dirstateFile); | 78 merged = new LinkedHashMap<Path, Record>(); |
91 try { | 79 |
92 if (da.isEmpty()) { | 80 DirstateReader dirstateReader = new DirstateReader(repo, pathPool); |
93 return; | 81 dirstateReader.readInto(new Inspector() { |
94 } | |
95 // not sure linked is really needed here, just for ease of debug | |
96 normal = new LinkedHashMap<Path, Record>(); | |
97 added = new LinkedHashMap<Path, Record>(); | |
98 removed = new LinkedHashMap<Path, Record>(); | |
99 merged = new LinkedHashMap<Path, Record>(); | |
100 | 82 |
101 parents = internalReadParents(da); | 83 public boolean next(EntryKind kind, Record r) { |
102 // hg init; hg up produces an empty repository where dirstate has parents (40 bytes) only | |
103 while (!da.isEmpty()) { | |
104 final byte state = da.readByte(); | |
105 final int fmode = da.readInt(); | |
106 final int size = da.readInt(); | |
107 final int time = da.readInt(); | |
108 final int nameLen = da.readInt(); | |
109 String fn1 = null, fn2 = null; | |
110 byte[] name = new byte[nameLen]; | |
111 da.readBytes(name, 0, nameLen); | |
112 for (int i = 0; i < nameLen; i++) { | |
113 if (name[i] == 0) { | |
114 fn1 = encodingHelper.fromDirstate(name, 0, i); | |
115 fn2 = encodingHelper.fromDirstate(name, i+1, nameLen - i - 1); | |
116 break; | |
117 } | |
118 } | |
119 if (fn1 == null) { | |
120 fn1 = encodingHelper.fromDirstate(name, 0, nameLen); | |
121 } | |
122 Record r = new Record(fmode, size, time, pathPool.path(fn1), fn2 == null ? null : pathPool.path(fn2)); | |
123 if (canonicalPathRewrite != null) { | 84 if (canonicalPathRewrite != null) { |
124 Path canonicalPath = pathPool.path(canonicalPathRewrite.rewrite(fn1).toString()); | 85 Path canonicalPath = pathPool.path(canonicalPathRewrite.rewrite(r.name())); |
125 if (canonicalPath != r.name()) { // == as they come from the same pool | 86 if (canonicalPath != r.name()) { // == as they come from the same pool |
126 assert !canonical2dirstateName.containsKey(canonicalPath); // otherwise there's already a file with same canonical name | 87 assert !canonical2dirstateName.containsKey(canonicalPath); // otherwise there's already a file with same canonical name |
127 // which can't happen for case-insensitive file system (or there's erroneous PathRewrite, perhaps doing smth else) | 88 // which can't happen for case-insensitive file system (or there's erroneous PathRewrite, perhaps doing smth else) |
128 canonical2dirstateName.put(canonicalPath, r.name()); | 89 canonical2dirstateName.put(canonicalPath, r.name()); |
129 } | 90 } |
130 if (fn2 != null) { | 91 if (r.copySource() != null) { |
131 // not sure I need copy origin in the map, I don't seem to use it anywhere, | 92 // not sure I need copy origin in the map, I don't seem to use it anywhere, |
132 // but I guess I'll have to use it some day. | 93 // but I guess I'll have to use it some day. |
133 canonicalPath = pathPool.path(canonicalPathRewrite.rewrite(fn2).toString()); | 94 canonicalPath = pathPool.path(canonicalPathRewrite.rewrite(r.copySource())); |
134 if (canonicalPath != r.copySource()) { | 95 if (canonicalPath != r.copySource()) { |
135 canonical2dirstateName.put(canonicalPath, r.copySource()); | 96 canonical2dirstateName.put(canonicalPath, r.copySource()); |
136 } | 97 } |
137 } | 98 } |
138 } | 99 } |
139 if (state == 'n') { | 100 switch (kind) { |
140 normal.put(r.name1, r); | 101 case Normal : normal.put(r.name(), r); break; |
141 } else if (state == 'a') { | 102 case Added : added.put(r.name(), r); break; |
142 added.put(r.name1, r); | 103 case Removed : removed.put(r.name(), r); break; |
143 } else if (state == 'r') { | 104 case Merged : merged.put(r.name1, r); break; |
144 removed.put(r.name1, r); | 105 default: throw new HgInvalidStateException(String.format("Unexpected entry in the dirstate: %s", kind)); |
145 } else if (state == 'm') { | |
146 merged.put(r.name1, r); | |
147 } else { | |
148 repo.getSessionContext().getLog().dump(getClass(), Severity.Warn, "Dirstate record for file %s (size: %d, tstamp:%d) has unknown state '%c'", r.name1, r.size(), r.time, state); | |
149 } | 106 } |
150 } | 107 return true; |
151 } catch (IOException ex) { | 108 } |
152 throw new HgInvalidControlFileException("Dirstate read failed", ex, dirstateFile); | 109 }); |
153 } finally { | 110 parents = dirstateReader.parents(); |
154 da.done(); | 111 } |
155 } | 112 |
156 } | |
157 | |
158 private static Pair<Nodeid, Nodeid> internalReadParents(DataAccess da) throws IOException { | |
159 byte[] parents = new byte[40]; | |
160 da.readBytes(parents, 0, 40); | |
161 Nodeid n1 = Nodeid.fromBinary(parents, 0); | |
162 Nodeid n2 = Nodeid.fromBinary(parents, 20); | |
163 parents = null; | |
164 return new Pair<Nodeid, Nodeid>(n1, n2); | |
165 } | |
166 | |
167 /** | 113 /** |
168 * @return pair of working copy parents, with {@link Nodeid#NULL} for missing values. | 114 * @return pair of working copy parents, with {@link Nodeid#NULL} for missing values. |
169 */ | 115 */ |
170 public Pair<Nodeid,Nodeid> parents() { | 116 public Pair<Nodeid,Nodeid> parents() { |
171 assert parents != null; // instance not initialized with #read() | 117 assert parents != null; // instance not initialized with #read() |
172 return parents; | 118 return parents; |
173 } | 119 } |
174 | 120 |
175 private static File getDirstateFile(Internals repo) { | |
176 return repo.getFileFromRepoDir(Dirstate.getName()); | |
177 } | |
178 | |
179 /** | |
180 * @return pair of parents, both {@link Nodeid#NULL} if dirstate is not available | |
181 */ | |
182 /*package-local*/ static Pair<Nodeid, Nodeid> readParents(Internals internalRepo) throws HgInvalidControlFileException { | |
183 // do not read whole dirstate if all we need is WC parent information | |
184 File dirstateFile = getDirstateFile(internalRepo); | |
185 if (dirstateFile == null || !dirstateFile.exists()) { | |
186 return new Pair<Nodeid,Nodeid>(NULL, NULL); | |
187 } | |
188 DataAccess da = internalRepo.getDataAccess().create(dirstateFile); | |
189 try { | |
190 if (da.isEmpty()) { | |
191 return new Pair<Nodeid,Nodeid>(NULL, NULL); | |
192 } | |
193 return internalReadParents(da); | |
194 } catch (IOException ex) { | |
195 throw new HgInvalidControlFileException("Error reading working copy parents from dirstate", ex, dirstateFile); | |
196 } finally { | |
197 da.done(); | |
198 } | |
199 } | |
200 | |
201 /** | |
202 * TODO [post-1.0] it's really not a proper place for the method, need WorkingCopyContainer or similar | |
203 * @return branch associated with the working directory | |
204 */ | |
205 /*package-local*/ static String readBranch(Internals internalRepo) throws HgInvalidControlFileException { | |
206 File branchFile = internalRepo.getFileFromRepoDir("branch"); | |
207 String branch = HgRepository.DEFAULT_BRANCH_NAME; | |
208 if (branchFile.exists()) { | |
209 try { | |
210 BufferedReader r = new BufferedReader(new FileReader(branchFile)); | |
211 String b = r.readLine(); | |
212 if (b != null) { | |
213 b = b.trim().intern(); | |
214 } | |
215 branch = b == null || b.length() == 0 ? HgRepository.DEFAULT_BRANCH_NAME : b; | |
216 r.close(); | |
217 } catch (FileNotFoundException ex) { | |
218 internalRepo.getSessionContext().getLog().dump(HgDirstate.class, Debug, ex, null); // log verbose debug, exception might be legal here | |
219 // IGNORE | |
220 } catch (IOException ex) { | |
221 throw new HgInvalidControlFileException("Error reading file with branch information", ex, branchFile); | |
222 } | |
223 } | |
224 return branch; | |
225 } | |
226 | |
227 // new, modifiable collection | 121 // new, modifiable collection |
228 /*package-local*/ TreeSet<Path> all() { | 122 /*package-local*/ TreeSet<Path> all() { |
229 assert normal != null; | 123 assert normal != null; |
230 TreeSet<Path> rv = new TreeSet<Path>(); | 124 TreeSet<Path> rv = new TreeSet<Path>(); |
231 @SuppressWarnings("unchecked") | 125 @SuppressWarnings("unchecked") |