comparison src/org/tmatesoft/hg/core/HgCloneCommand.java @ 532:688c1ab113bb

Introduce explicit reference to base patch in bundle's group element, use it when cloning to fix defect when few revisions list null,null parents
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 23 Jan 2013 19:14:15 +0100
parents 95c2f43008bd
children 243202f1bda5
comparison
equal deleted inserted replaced
531:95c2f43008bd 532:688c1ab113bb
145 private final TreeMap<Nodeid, Integer> changelogIndexes = new TreeMap<Nodeid, Integer>(); 145 private final TreeMap<Nodeid, Integer> changelogIndexes = new TreeMap<Nodeid, Integer>();
146 private boolean collectChangelogIndexes = false; 146 private boolean collectChangelogIndexes = false;
147 147
148 private DataAccess prevRevContent; 148 private DataAccess prevRevContent;
149 private final DigestHelper dh = new DigestHelper(); 149 private final DigestHelper dh = new DigestHelper();
150 private final ArrayList<Nodeid> revisionSequence = new ArrayList<Nodeid>(); // last visited nodes first 150 // recently processed nodes last, so that index in the array may be used as a linkRevision or baseRevision
151 private final ArrayList<Nodeid> revisionSequence = new ArrayList<Nodeid>();
151 152
152 private final LinkedList<String> fncacheFiles = new LinkedList<String>(); 153 private final LinkedList<String> fncacheFiles = new LinkedList<String>();
153 private RepoInitializer repoInit; 154 private RepoInitializer repoInit;
154 private Lifecycle.Callback lifecycleCallback; 155 private Lifecycle.Callback lifecycleCallback;
155 private CancelledException cancelException; 156 private CancelledException cancelException;
181 revlogHeader.offset(0).baseRevision(-1); 182 revlogHeader.offset(0).baseRevision(-1);
182 revisionSequence.clear(); 183 revisionSequence.clear();
183 indexFile = new FileOutputStream(new File(hgDir, filename = "store/00changelog.i")); 184 indexFile = new FileOutputStream(new File(hgDir, filename = "store/00changelog.i"));
184 collectChangelogIndexes = true; 185 collectChangelogIndexes = true;
185 } catch (IOException ex) { 186 } catch (IOException ex) {
186 throw new HgInvalidControlFileException("Failed to write changelog", ex, new File(filename)); 187 throw new HgInvalidControlFileException("Failed to write changelog", ex, new File(hgDir, filename));
187 } 188 }
188 stopIfCancelled(); 189 stopIfCancelled();
189 } 190 }
190 191
191 public void changelogEnd() { 192 public void changelogEnd() {
192 try { 193 try {
193 if (prevRevContent != null) { 194 clearPreviousContent();
194 prevRevContent.done();
195 prevRevContent = null;
196 }
197 collectChangelogIndexes = false; 195 collectChangelogIndexes = false;
198 indexFile.close(); 196 closeIndexFile();
199 indexFile = null; 197 } catch (IOException ex) {
200 filename = null; 198 throw new HgInvalidControlFileException("Failed to write changelog", ex, new File(hgDir, filename));
201 } catch (IOException ex) {
202 throw new HgInvalidControlFileException("Failed to write changelog", ex, new File(filename));
203 } 199 }
204 progressSupport.worked(1); 200 progressSupport.worked(1);
205 stopIfCancelled(); 201 stopIfCancelled();
206 } 202 }
207 203
209 try { 205 try {
210 revlogHeader.offset(0).baseRevision(-1); 206 revlogHeader.offset(0).baseRevision(-1);
211 revisionSequence.clear(); 207 revisionSequence.clear();
212 indexFile = new FileOutputStream(new File(hgDir, filename = "store/00manifest.i")); 208 indexFile = new FileOutputStream(new File(hgDir, filename = "store/00manifest.i"));
213 } catch (IOException ex) { 209 } catch (IOException ex) {
214 throw new HgInvalidControlFileException("Failed to write manifest", ex, new File(filename)); 210 throw new HgInvalidControlFileException("Failed to write manifest", ex, new File(hgDir, filename));
215 } 211 }
216 stopIfCancelled(); 212 stopIfCancelled();
217 } 213 }
218 214
219 public void manifestEnd() { 215 public void manifestEnd() {
220 try { 216 try {
221 if (prevRevContent != null) { 217 clearPreviousContent();
222 prevRevContent.done(); 218 closeIndexFile();
223 prevRevContent = null; 219 } catch (IOException ex) {
224 } 220 throw new HgInvalidControlFileException("Failed to write manifest", ex, new File(hgDir, filename));
225 indexFile.close();
226 indexFile = null;
227 filename = null;
228 } catch (IOException ex) {
229 throw new HgInvalidControlFileException("Failed to write changelog", ex, new File(filename));
230 } 221 }
231 progressSupport.worked(1); 222 progressSupport.worked(1);
232 stopIfCancelled(); 223 stopIfCancelled();
233 } 224 }
234 225
248 stopIfCancelled(); 239 stopIfCancelled();
249 } 240 }
250 241
251 public void fileEnd(String name) { 242 public void fileEnd(String name) {
252 try { 243 try {
253 if (prevRevContent != null) { 244 clearPreviousContent();
254 prevRevContent.done(); 245 closeIndexFile();
255 prevRevContent = null;
256 }
257 indexFile.close();
258 indexFile = null;
259 filename = null;
260 } catch (IOException ex) { 246 } catch (IOException ex) {
261 String m = String.format("Failed to write file %s", filename); 247 String m = String.format("Failed to write file %s", filename);
262 throw new HgInvalidControlFileException(m, ex, new File(filename)); 248 throw new HgInvalidControlFileException(m, ex, new File(filename));
263 } 249 }
264 progressSupport.worked(1); 250 progressSupport.worked(1);
265 stopIfCancelled(); 251 stopIfCancelled();
252 }
253
254 private void clearPreviousContent() {
255 if (prevRevContent != null) {
256 prevRevContent.done();
257 prevRevContent = null;
258 }
259 }
260
261 private void closeIndexFile() throws IOException {
262 indexFile.close();
263 indexFile = null;
264 filename = null;
266 } 265 }
267 266
268 private int knownRevision(Nodeid p) { 267 private int knownRevision(Nodeid p) {
269 if (p.isNull()) { 268 if (p.isNull()) {
270 return -1; 269 return -1;
274 return i; 273 return i;
275 } 274 }
276 } 275 }
277 } 276 }
278 String m = String.format("Can't find index of %s for file %s", p.shortNotation(), filename); 277 String m = String.format("Can't find index of %s for file %s", p.shortNotation(), filename);
279 throw new HgInvalidControlFileException(m, null, null).setRevision(p); 278 throw new HgInvalidControlFileException(m, null, new File(hgDir, filename)).setRevision(p);
280 } 279 }
281 280
282 private RevlogStreamWriter.HeaderWriter revlogHeader = new RevlogStreamWriter.HeaderWriter(true); 281 private RevlogStreamWriter.HeaderWriter revlogHeader = new RevlogStreamWriter.HeaderWriter(true);
283 private RevlogCompressor revlogDataZip = new RevlogCompressor(); 282 private RevlogCompressor revlogDataZip = new RevlogCompressor();
284 283
285 public boolean element(GroupElement ge) { 284 public boolean element(GroupElement ge) {
286 try { 285 try {
287 assert indexFile != null; 286 assert indexFile != null;
288 boolean writeComplete = false; 287 boolean writeComplete = false;
288 Nodeid deltaBase = ge.patchBase();
289 if (deltaBase.isNull()) {
290 // NOTE, can't use both parents isNull == true to empty prevRevContent
291 // see build.gradle sample below why.
292 prevRevContent = new DataAccess(); // empty data
293 writeComplete = true;
294 // if (writeComplete) would set baseRevision correctly,
295 } else {
296 Nodeid prevRevision = revisionSequence.size() > 0 ? revisionSequence.get(revisionSequence.size()-1) : Nodeid.NULL;
297 if (!prevRevision.equals(deltaBase)) {
298 // presently, bundle group elements always patch previous, see
299 // (a) changegroup.py#builddeltaheader(): # do nothing with basenode, it is implicitly the previous one in HG10
300 // (b) revlog.py#group(): prev, curr = revs[r], revs[r + 1]
301 // for c in bundler.revchunk(self, curr, prev):
302 // so there's no reason to have code here to extract contents of deltaBase revision
303 String m = String.format("Revision %s import failed: delta base %s is not the last node we've handled (and know content for) %s", ge.node(), deltaBase, prevRevision);
304 throw new HgInvalidStateException(m);
305 }
306 }
307 //
308 byte[] content = ge.apply(prevRevContent.byteArray());
289 Nodeid p1 = ge.firstParent(); 309 Nodeid p1 = ge.firstParent();
290 Nodeid p2 = ge.secondParent(); 310 Nodeid p2 = ge.secondParent();
291 if (p1.isNull() && p2.isNull() /* or forced flag, does REVIDX_PUNCHED_FLAG indicate that? */) {
292 // FIXME NOTE, both parents isNull == true doesn't necessarily mean
293 // empty prevContent, see build.gradle sample below
294 prevRevContent = new ByteArrayDataAccess(new byte[0]);
295 writeComplete = true;
296 }
297 byte[] content = ge.apply(prevRevContent.byteArray());
298 byte[] calculated = dh.sha1(p1, p2, content).asBinary(); 311 byte[] calculated = dh.sha1(p1, p2, content).asBinary();
299 final Nodeid node = ge.node(); 312 final Nodeid node = ge.node();
300 if (!node.equalsTo(calculated)) { 313 if (!node.equalsTo(calculated)) {
301 String m = String.format("Checksum failed: expected %s, calculated %s. File %s", node, calculated, filename); 314 String m = String.format("Checksum failed: expected %s, calculated %s. File %s", node, calculated, filename);
302 throw new HgRevisionIntegrityException(m, null, new File(hgDir, filename)); 315 throw new HgRevisionIntegrityException(m, null, new File(hgDir, filename));
303 } 316 }
304 revlogHeader.nodeid(node); 317 revlogHeader.nodeid(node);
318 //
305 if (collectChangelogIndexes) { 319 if (collectChangelogIndexes) {
306 changelogIndexes.put(node, revisionSequence.size()); 320 changelogIndexes.put(node, revisionSequence.size());
307 revlogHeader.linkRevision(revisionSequence.size()); 321 revlogHeader.linkRevision(revisionSequence.size());
308 } else { 322 } else {
309 Integer csRev = changelogIndexes.get(ge.cset()); 323 Integer csRev = changelogIndexes.get(ge.cset());
310 if (csRev == null) { 324 if (csRev == null) {
311 throw new HgInvalidStateException(String.format("Changelog doesn't contain revision %s of %s", ge.cset().shortNotation(), filename)); 325 throw new HgInvalidStateException(String.format("Changelog doesn't contain revision %s of %s", ge.cset().shortNotation(), filename));
312 } 326 }
313 revlogHeader.linkRevision(csRev.intValue()); 327 revlogHeader.linkRevision(csRev.intValue());
314 } 328 }
329 //
315 revlogHeader.parents(knownRevision(p1), knownRevision(p2)); 330 revlogHeader.parents(knownRevision(p1), knownRevision(p2));
331 //
316 byte[] patchContent = ge.rawDataByteArray(); 332 byte[] patchContent = ge.rawDataByteArray();
333 // no reason to keep patch if it's close (here, >75%) in size to the complete contents,
334 // save patching effort in this case
317 writeComplete = writeComplete || patchContent.length >= (/* 3/4 of actual */content.length - (content.length >>> 2)); 335 writeComplete = writeComplete || patchContent.length >= (/* 3/4 of actual */content.length - (content.length >>> 2));
336
318 if (writeComplete) { 337 if (writeComplete) {
319 revlogHeader.baseRevision(revisionSequence.size()); 338 revlogHeader.baseRevision(revisionSequence.size());
320 } 339 }
340 assert revlogHeader.baseRevision() >= 0;
341
321 final byte[] sourceData = writeComplete ? content : patchContent; 342 final byte[] sourceData = writeComplete ? content : patchContent;
322 revlogDataZip.reset(sourceData); 343 revlogDataZip.reset(sourceData);
323 final int compressedLen; 344 final int compressedLen;
324 final boolean useUncompressedData = revlogDataZip.getCompressedLengthEstimate() >= (sourceData.length - (sourceData.length >>> 2)); 345 final boolean useUncompressedData = revlogDataZip.getCompressedLengthEstimate() >= (sourceData.length - (sourceData.length >>> 2));
325 if (useUncompressedData) { 346 if (useUncompressedData) {
376 d0be588453068787dcb3ee05f8edfe47fdd5ae78 4011d52141cd717c92cbf350a93522d2f3ee415e 0000000000000000000000000000000000000000 ad0322a4af204547c400e1846b2b83d446ab8da5 4011d52141cd717c92cbf350a93522d2f3ee415e 85 397 d0be588453068787dcb3ee05f8edfe47fdd5ae78 4011d52141cd717c92cbf350a93522d2f3ee415e 0000000000000000000000000000000000000000 ad0322a4af204547c400e1846b2b83d446ab8da5 4011d52141cd717c92cbf350a93522d2f3ee415e 85
377 3ddd456244a08f81779163d9faf922a6dcd9e53e d0be588453068787dcb3ee05f8edfe47fdd5ae78 0000000000000000000000000000000000000000 3ace1fc95d0a1a941b6427c60b6e624f96dd71ad d0be588453068787dcb3ee05f8edfe47fdd5ae78 151 398 3ddd456244a08f81779163d9faf922a6dcd9e53e d0be588453068787dcb3ee05f8edfe47fdd5ae78 0000000000000000000000000000000000000000 3ace1fc95d0a1a941b6427c60b6e624f96dd71ad d0be588453068787dcb3ee05f8edfe47fdd5ae78 151
378 a3f374fbf33aba1cc3b4f472db022b5185880f5d 3ddd456244a08f81779163d9faf922a6dcd9e53e 0000000000000000000000000000000000000000 3ca4ae7bdd3890b8ed89bfea1b42af593e04b373 3ddd456244a08f81779163d9faf922a6dcd9e53e 195 399 a3f374fbf33aba1cc3b4f472db022b5185880f5d 3ddd456244a08f81779163d9faf922a6dcd9e53e 0000000000000000000000000000000000000000 3ca4ae7bdd3890b8ed89bfea1b42af593e04b373 3ddd456244a08f81779163d9faf922a6dcd9e53e 195
379 0227d28e0db69afebee34cd5a4151889fb6271da a3f374fbf33aba1cc3b4f472db022b5185880f5d 0000000000000000000000000000000000000000 31bd09da0dcfe48e1fc662143f91ff402238aa84 a3f374fbf33aba1cc3b4f472db022b5185880f5d 145 400 0227d28e0db69afebee34cd5a4151889fb6271da a3f374fbf33aba1cc3b4f472db022b5185880f5d 0000000000000000000000000000000000000000 31bd09da0dcfe48e1fc662143f91ff402238aa84 a3f374fbf33aba1cc3b4f472db022b5185880f5d 145
380 401
381 but there's no delta base information in the bundle file, it's merely a hard-coded convention (always patches previous version, see 402 but there's no delta base information in the bundle file, it's merely a hard-coded convention
382 (a) changegroup.py#builddeltaheader(): # do nothing with basenode, it is implicitly the previous one in HG10
383 (b) revlog.py#group(): prev, curr = revs[r], revs[r + 1]
384 for c in bundler.revchunk(self, curr, prev):
385 )
386
387 403
388 It's unclear where the first chunk (identified 62a101b7...) comes from (by the way, there's no such changeset as 6ec4af... as specified in the chunk, while 7dcc920e.. IS changeset 454) 404 It's unclear where the first chunk (identified 62a101b7...) comes from (by the way, there's no such changeset as 6ec4af... as specified in the chunk, while 7dcc920e.. IS changeset 454)
389 405
390 EXPLANATION: 406 EXPLANATION:
391 if cloned repository comes from svnkit repo (where's the gradle branch): 407 if cloned repository comes from svnkit repo (where's the gradle branch):