comparison src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java @ 397:5e95b0da26f2 smartgit3

Issue 24: IAE, Underflow in FilterDataAccess. Issue 26:UnsupportedOperationException when patching empty base revision. Tests
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 23 Feb 2012 15:31:57 +0100
parents 2fadf8695f8a
children 866fc3b597a0
comparison
equal deleted inserted replaced
393:728708de3597 397:5e95b0da26f2
1 /* 1 /*
2 * Copyright (c) 2011 TMate Software Ltd 2 * Copyright (c) 2011-2012 TMate Software Ltd
3 * 3 *
4 * This program is free software; you can redistribute it and/or modify 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 5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 2 of the License. 6 * the Free Software Foundation; version 2 of the License.
7 * 7 *
28 import java.util.Collections; 28 import java.util.Collections;
29 import java.util.NoSuchElementException; 29 import java.util.NoSuchElementException;
30 import java.util.Set; 30 import java.util.Set;
31 import java.util.TreeSet; 31 import java.util.TreeSet;
32 32
33 import org.tmatesoft.hg.core.HgBadStateException;
34 import org.tmatesoft.hg.core.HgException; 33 import org.tmatesoft.hg.core.HgException;
35 import org.tmatesoft.hg.core.HgInvalidControlFileException; 34 import org.tmatesoft.hg.core.HgInvalidControlFileException;
35 import org.tmatesoft.hg.core.HgInvalidFileException;
36 import org.tmatesoft.hg.core.Nodeid; 36 import org.tmatesoft.hg.core.Nodeid;
37 import org.tmatesoft.hg.internal.ByteArrayChannel; 37 import org.tmatesoft.hg.internal.ByteArrayChannel;
38 import org.tmatesoft.hg.internal.Experimental; 38 import org.tmatesoft.hg.internal.Experimental;
39 import org.tmatesoft.hg.internal.FilterByteChannel; 39 import org.tmatesoft.hg.internal.FilterByteChannel;
40 import org.tmatesoft.hg.internal.ManifestRevision; 40 import org.tmatesoft.hg.internal.ManifestRevision;
289 if (timestampEqual && sizeEqual) { 289 if (timestampEqual && sizeEqual) {
290 inspector.clean(fname); 290 inspector.clean(fname);
291 } else if (!sizeEqual && r.size() >= 0) { 291 } else if (!sizeEqual && r.size() >= 0) {
292 inspector.modified(fname); 292 inspector.modified(fname);
293 } else { 293 } else {
294 // size is the same or unknown, and, perhaps, different timestamp 294 try {
295 // check actual content to avoid false modified files 295 // size is the same or unknown, and, perhaps, different timestamp
296 HgDataFile df = repo.getFileNode(fname); 296 // check actual content to avoid false modified files
297 if (!df.exists()) { 297 HgDataFile df = repo.getFileNode(fname);
298 String msg = String.format("File %s known as normal in dirstate (%d, %d), doesn't exist at %s", fname, r.modificationTime(), r.size(), repo.getStoragePath(df)); 298 if (!df.exists()) {
299 throw new HgBadStateException(msg); 299 String msg = String.format("File %s known as normal in dirstate (%d, %d), doesn't exist at %s", fname, r.modificationTime(), r.size(), repo.getStoragePath(df));
300 } 300 throw new HgInvalidFileException(msg, null).setFileName(fname);
301 Nodeid rev = getDirstateParentManifest().nodeid(fname); 301 }
302 // rev might be null here if fname comes to dirstate as a result of a merge operation 302 Nodeid rev = getDirstateParentManifest().nodeid(fname);
303 // where one of the parents (first parent) had no fname file, but second parent had. 303 // rev might be null here if fname comes to dirstate as a result of a merge operation
304 // E.g. fork revision 3, revision 4 gets .hgtags, few modifications and merge(3,12) 304 // where one of the parents (first parent) had no fname file, but second parent had.
305 // see Issue 14 for details 305 // E.g. fork revision 3, revision 4 gets .hgtags, few modifications and merge(3,12)
306 if (rev == null || !areTheSame(f, df, rev)) { 306 // see Issue 14 for details
307 inspector.modified(df.getPath()); 307 if (rev == null || !areTheSame(f, df, rev)) {
308 } else { 308 inspector.modified(df.getPath());
309 inspector.clean(df.getPath()); 309 } else {
310 inspector.clean(df.getPath());
311 }
312 } catch (HgException ex) {
313 repo.getContext().getLog().warn(getClass(), ex, null);
314 inspector.invalid(fname, ex);
310 } 315 }
311 } 316 }
312 } else if ((r = getDirstateImpl().checkAdded(fname)) != null) { 317 } else if ((r = getDirstateImpl().checkAdded(fname)) != null) {
313 if (r.copySource() == null) { 318 if (r.copySource() == null) {
314 inspector.added(fname); 319 inspector.added(fname);
381 // otherwise, shall check actual content (size not the same, or unknown (-1 or -2), or timestamp is different, 386 // otherwise, shall check actual content (size not the same, or unknown (-1 or -2), or timestamp is different,
382 // or nodeid in dirstate is different, but local change might have brought it back to baseRevision state) 387 // or nodeid in dirstate is different, but local change might have brought it back to baseRevision state)
383 // FALL THROUGH 388 // FALL THROUGH
384 } 389 }
385 if (r != null || (r = getDirstateImpl().checkMerged(fname)) != null || (r = getDirstateImpl().checkAdded(fname)) != null) { 390 if (r != null || (r = getDirstateImpl().checkMerged(fname)) != null || (r = getDirstateImpl().checkAdded(fname)) != null) {
386 // check actual content to see actual changes 391 try {
387 // when added - seems to be the case of a file added once again, hence need to check if content is different 392 // check actual content to see actual changes
388 // either clean or modified 393 // when added - seems to be the case of a file added once again, hence need to check if content is different
389 HgDataFile fileNode = repo.getFileNode(fname); 394 // either clean or modified
390 if (areTheSame(f, fileNode, nid1)) { 395 HgDataFile fileNode = repo.getFileNode(fname);
391 inspector.clean(fname); 396 if (areTheSame(f, fileNode, nid1)) {
392 } else { 397 inspector.clean(fname);
393 inspector.modified(fname); 398 } else {
399 inspector.modified(fname);
400 }
401 } catch (HgException ex) {
402 repo.getContext().getLog().warn(getClass(), ex, null);
403 inspector.invalid(fname, ex);
394 } 404 }
395 baseRevNames.remove(fname); // consumed, processed, handled. 405 baseRevNames.remove(fname); // consumed, processed, handled.
396 } else if (getDirstateImpl().checkRemoved(fname) != null) { 406 } else if (getDirstateImpl().checkRemoved(fname) != null) {
397 // was known, and now marked as removed, report it right away, do not rely on baseRevNames processing later 407 // was known, and now marked as removed, report it right away, do not rely on baseRevNames processing later
398 inspector.removed(fname); 408 inspector.removed(fname);
407 // changeset nodeid + hash(actual content) => entry (Nodeid) in the next Manifest 417 // changeset nodeid + hash(actual content) => entry (Nodeid) in the next Manifest
408 // then it's sufficient to check parents from dirstate, and if they do not match parents from file's baseRevision (non matching parents means different nodeids). 418 // then it's sufficient to check parents from dirstate, and if they do not match parents from file's baseRevision (non matching parents means different nodeids).
409 // The question is whether original Hg treats this case (same content, different parents and hence nodeids) as 'modified' or 'clean' 419 // The question is whether original Hg treats this case (same content, different parents and hence nodeids) as 'modified' or 'clean'
410 } 420 }
411 421
412 private boolean areTheSame(FileInfo f, HgDataFile dataFile, Nodeid revision) { 422 private boolean areTheSame(FileInfo f, HgDataFile dataFile, Nodeid revision) throws HgException {
413 // XXX consider adding HgDataDile.compare(File/byte[]/whatever) operation to optimize comparison 423 // XXX consider adding HgDataDile.compare(File/byte[]/whatever) operation to optimize comparison
414 ByteArrayChannel bac = new ByteArrayChannel(); 424 ByteArrayChannel bac = new ByteArrayChannel();
415 boolean ioFailed = false;
416 try { 425 try {
417 int fileRevisionIndex = dataFile.getRevisionIndex(revision); 426 int fileRevisionIndex = dataFile.getRevisionIndex(revision);
418 // need content with metadata striped off - although theoretically chances are metadata may be different, 427 // need content with metadata striped off - although theoretically chances are metadata may be different,
419 // WC doesn't have it anyway 428 // WC doesn't have it anyway
420 dataFile.content(fileRevisionIndex, bac); 429 dataFile.content(fileRevisionIndex, bac);
421 } catch (CancelledException ex) { 430 } catch (CancelledException ex) {
422 // silently ignore - can't happen, ByteArrayChannel is not cancellable 431 // silently ignore - can't happen, ByteArrayChannel is not cancellable
423 } catch (HgException ex) { 432 }
424 repo.getContext().getLog().warn(getClass(), ex, null); 433 return areTheSame(f, bac.toArray(), dataFile.getPath());
425 ioFailed = true; 434 }
426 } 435
427 return !ioFailed && areTheSame(f, bac.toArray(), dataFile.getPath()); 436 private boolean areTheSame(FileInfo f, final byte[] data, Path p) throws HgException {
428 }
429
430 private boolean areTheSame(FileInfo f, final byte[] data, Path p) {
431 ReadableByteChannel is = null; 437 ReadableByteChannel is = null;
432 class Check implements ByteChannel { 438 class Check implements ByteChannel {
433 final boolean debug = repo.getContext().getLog().isDebug(); 439 final boolean debug = repo.getContext().getLog().isDebug();
434 boolean sameSoFar = true; 440 boolean sameSoFar = true;
435 int x = 0; 441 int x = 0;
496 return check.ultimatelyTheSame(); 502 return check.ultimatelyTheSame();
497 } catch (CancelledException ex) { 503 } catch (CancelledException ex) {
498 repo.getContext().getLog().warn(getClass(), ex, "Unexpected cancellation"); 504 repo.getContext().getLog().warn(getClass(), ex, "Unexpected cancellation");
499 return check.ultimatelyTheSame(); 505 return check.ultimatelyTheSame();
500 } catch (IOException ex) { 506 } catch (IOException ex) {
501 repo.getContext().getLog().warn(getClass(), ex, null); 507 throw new HgInvalidFileException("File comparison failed", ex).setFileName(p);
502 } finally { 508 } finally {
503 if (is != null) { 509 if (is != null) {
504 try { 510 try {
505 is.close(); 511 is.close();
506 } catch (IOException ex) { 512 } catch (IOException ex) {
507 repo.getContext().getLog().info(getClass(), ex, null); 513 repo.getContext().getLog().info(getClass(), ex, null);
508 } 514 }
509 } 515 }
510 } 516 }
511 return false;
512 } 517 }
513 518
514 private static boolean todoCheckFlagsEqual(FileInfo f, HgManifest.Flags originalManifestFlags) { 519 private static boolean todoCheckFlagsEqual(FileInfo f, HgManifest.Flags originalManifestFlags) {
515 // FIXME implement 520 // FIXME implement
516 return true; 521 return true;