Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java @ 413:7f27122011c3
Support and respect for symbolic links and executable flag, with /bin/ls backed implementation to discover these
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Wed, 21 Mar 2012 20:40:28 +0100 |
| parents | 866fc3b597a0 |
| children | ee8264d80747 |
comparison
equal
deleted
inserted
replaced
| 406:d56ea1a2537a | 413:7f27122011c3 |
|---|---|
| 35 import org.tmatesoft.hg.core.HgInvalidFileException; | 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.Internals; | |
| 40 import org.tmatesoft.hg.internal.ManifestRevision; | 41 import org.tmatesoft.hg.internal.ManifestRevision; |
| 41 import org.tmatesoft.hg.internal.PathScope; | 42 import org.tmatesoft.hg.internal.PathScope; |
| 42 import org.tmatesoft.hg.internal.Preview; | 43 import org.tmatesoft.hg.internal.Preview; |
| 43 import org.tmatesoft.hg.util.Adaptable; | 44 import org.tmatesoft.hg.util.Adaptable; |
| 44 import org.tmatesoft.hg.util.ByteChannel; | 45 import org.tmatesoft.hg.util.ByteChannel; |
| 285 HgDirstate.Record r; | 286 HgDirstate.Record r; |
| 286 if ((r = getDirstateImpl().checkNormal(fname)) != null) { | 287 if ((r = getDirstateImpl().checkNormal(fname)) != null) { |
| 287 // either clean or modified | 288 // either clean or modified |
| 288 final boolean timestampEqual = f.lastModified() == r.modificationTime(), sizeEqual = r.size() == f.length(); | 289 final boolean timestampEqual = f.lastModified() == r.modificationTime(), sizeEqual = r.size() == f.length(); |
| 289 if (timestampEqual && sizeEqual) { | 290 if (timestampEqual && sizeEqual) { |
| 290 inspector.clean(fname); | 291 // if flags change (chmod -x), timestamp does not change |
| 292 if (checkFlagsEqual(f, r.mode())) { | |
| 293 inspector.clean(fname); | |
| 294 } else { | |
| 295 inspector.modified(fname); // flags are not the same | |
| 296 } | |
| 291 } else if (!sizeEqual && r.size() >= 0) { | 297 } else if (!sizeEqual && r.size() >= 0) { |
| 292 inspector.modified(fname); | 298 inspector.modified(fname); |
| 293 } else { | 299 } else { |
| 300 // size is the same or unknown, and, perhaps, different timestamp | |
| 301 // check actual content to avoid false modified files | |
| 294 try { | 302 try { |
| 295 // size is the same or unknown, and, perhaps, different timestamp | 303 if (!checkFlagsEqual(f, r.mode())) { |
| 296 // check actual content to avoid false modified files | 304 // flags modified, no need to do expensive content check |
| 297 HgDataFile df = repo.getFileNode(fname); | 305 inspector.modified(fname); |
| 298 if (!df.exists()) { | |
| 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 throw new HgInvalidFileException(msg, null).setFileName(fname); | |
| 301 } | |
| 302 Nodeid rev = getDirstateParentManifest().nodeid(fname); | |
| 303 // rev might be null here if fname comes to dirstate as a result of a merge operation | |
| 304 // where one of the parents (first parent) had no fname file, but second parent had. | |
| 305 // E.g. fork revision 3, revision 4 gets .hgtags, few modifications and merge(3,12) | |
| 306 // see Issue 14 for details | |
| 307 if (rev == null || !areTheSame(f, df, rev)) { | |
| 308 inspector.modified(df.getPath()); | |
| 309 } else { | 306 } else { |
| 310 inspector.clean(df.getPath()); | 307 HgDataFile df = repo.getFileNode(fname); |
| 308 if (!df.exists()) { | |
| 309 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)); | |
| 310 throw new HgInvalidFileException(msg, null).setFileName(fname); | |
| 311 } | |
| 312 Nodeid rev = getDirstateParentManifest().nodeid(fname); | |
| 313 // rev might be null here if fname comes to dirstate as a result of a merge operation | |
| 314 // where one of the parents (first parent) had no fname file, but second parent had. | |
| 315 // E.g. fork revision 3, revision 4 gets .hgtags, few modifications and merge(3,12) | |
| 316 // see Issue 14 for details | |
| 317 if (rev == null || !areTheSame(f, df, rev)) { | |
| 318 inspector.modified(df.getPath()); | |
| 319 } else { | |
| 320 inspector.clean(df.getPath()); | |
| 321 } | |
| 311 } | 322 } |
| 312 } catch (HgException ex) { | 323 } catch (HgException ex) { |
| 313 repo.getContext().getLog().warn(getClass(), ex, null); | 324 repo.getContext().getLog().warn(getClass(), ex, null); |
| 314 inspector.invalid(fname, ex); | 325 inspector.invalid(fname, ex); |
| 315 } | 326 } |
| 372 inspector.clean(fname); | 383 inspector.clean(fname); |
| 373 handled = true; | 384 handled = true; |
| 374 } else if (!sizeEqual && r.size() >= 0) { | 385 } else if (!sizeEqual && r.size() >= 0) { |
| 375 inspector.modified(fname); | 386 inspector.modified(fname); |
| 376 handled = true; | 387 handled = true; |
| 377 } else if (!todoCheckFlagsEqual(f, flags)) { | 388 } else if (!checkFlagsEqual(f, flags)) { |
| 378 // seems like flags have changed, no reason to check content further | 389 // seems like flags have changed, no reason to check content further |
| 379 inspector.modified(fname); | 390 inspector.modified(fname); |
| 380 handled = true; | 391 handled = true; |
| 381 } | 392 } |
| 382 if (handled) { | 393 if (handled) { |
| 514 } | 525 } |
| 515 } | 526 } |
| 516 } | 527 } |
| 517 } | 528 } |
| 518 | 529 |
| 519 private static boolean todoCheckFlagsEqual(FileInfo f, HgManifest.Flags originalManifestFlags) { | 530 /** |
| 520 // FIXME implement | 531 * @return <code>true</code> if flags are the same |
| 521 return true; | 532 */ |
| 533 private boolean checkFlagsEqual(FileInfo f, HgManifest.Flags originalManifestFlags) { | |
| 534 boolean same = true; | |
| 535 if (repoWalker.supportsLinkFlag()) { | |
| 536 if (originalManifestFlags == HgManifest.Flags.Link) { | |
| 537 return f.isSymlink(); | |
| 538 } | |
| 539 // original flag is not link, hence flags are the same if file is not link, too. | |
| 540 same = !f.isSymlink(); | |
| 541 } // otherwise treat flags the same | |
| 542 if (repoWalker.supportsExecFlag()) { | |
| 543 if (originalManifestFlags == HgManifest.Flags.Exec) { | |
| 544 return f.isExecutable(); | |
| 545 } | |
| 546 // original flag has no executable attribute, hence file shall not be executable, too | |
| 547 same = same || !f.isExecutable(); | |
| 548 } | |
| 549 return same; | |
| 550 } | |
| 551 | |
| 552 private boolean checkFlagsEqual(FileInfo f, int dirstateFileMode) { | |
| 553 // source/include/linux/stat.h | |
| 554 final int S_IFLNK = 0120000, S_IXUSR = 00100; | |
| 555 // TODO post-1.0 HgManifest.Flags.parse(int) | |
| 556 if ((dirstateFileMode & S_IFLNK) == S_IFLNK) { | |
| 557 return checkFlagsEqual(f, HgManifest.Flags.Link); | |
| 558 } | |
| 559 if ((dirstateFileMode & S_IXUSR) == S_IXUSR) { | |
| 560 return checkFlagsEqual(f, HgManifest.Flags.Exec); | |
| 561 } | |
| 562 return checkFlagsEqual(f, null); // no flags | |
| 522 } | 563 } |
| 523 | 564 |
| 524 /** | 565 /** |
| 525 * Configure status collector to consider only subset of a working copy tree. Tries to be as effective as possible, and to | 566 * Configure status collector to consider only subset of a working copy tree. Tries to be as effective as possible, and to |
| 526 * traverse only relevant part of working copy on the filesystem. | 567 * traverse only relevant part of working copy on the filesystem. |
| 578 private static class FileListIterator implements FileIterator { | 619 private static class FileListIterator implements FileIterator { |
| 579 private final File dir; | 620 private final File dir; |
| 580 private final Path[] paths; | 621 private final Path[] paths; |
| 581 private int index; | 622 private int index; |
| 582 private RegularFileInfo nextFile; | 623 private RegularFileInfo nextFile; |
| 624 private final boolean execCap, linkCap; | |
| 583 | 625 |
| 584 public FileListIterator(File startDir, Path... files) { | 626 public FileListIterator(File startDir, Path... files) { |
| 585 dir = startDir; | 627 dir = startDir; |
| 586 paths = files; | 628 paths = files; |
| 587 reset(); | 629 reset(); |
| 630 execCap = Internals.checkSupportsExecutables(startDir); | |
| 631 linkCap = Internals.checkSupportsSymlinks(startDir); | |
| 588 } | 632 } |
| 589 | 633 |
| 590 public void reset() { | 634 public void reset() { |
| 591 index = -1; | 635 index = -1; |
| 592 nextFile = new RegularFileInfo(); | 636 nextFile = new RegularFileInfo(execCap, linkCap); |
| 593 } | 637 } |
| 594 | 638 |
| 595 public boolean hasNext() { | 639 public boolean hasNext() { |
| 596 return paths.length > 0 && index < paths.length-1; | 640 return paths.length > 0 && index < paths.length-1; |
| 597 } | 641 } |
| 616 for (int i = 0; i < paths.length; i++) { | 660 for (int i = 0; i < paths.length; i++) { |
| 617 if (paths[i].equals(file)) { | 661 if (paths[i].equals(file)) { |
| 618 return true; | 662 return true; |
| 619 } | 663 } |
| 620 } | 664 } |
| 665 return false; | |
| 666 } | |
| 667 | |
| 668 public boolean supportsExecFlag() { | |
| 669 // TODO Auto-generated method stub | |
| 670 return false; | |
| 671 } | |
| 672 | |
| 673 public boolean supportsLinkFlag() { | |
| 674 // TODO Auto-generated method stub | |
| 621 return false; | 675 return false; |
| 622 } | 676 } |
| 623 } | 677 } |
| 624 | 678 |
| 625 private static class FileIteratorFilter implements FileIterator { | 679 private static class FileIteratorFilter implements FileIterator { |
| 668 } | 722 } |
| 669 | 723 |
| 670 public boolean inScope(Path file) { | 724 public boolean inScope(Path file) { |
| 671 return filter.accept(file); | 725 return filter.accept(file); |
| 672 } | 726 } |
| 727 | |
| 728 public boolean supportsExecFlag() { | |
| 729 return walker.supportsExecFlag(); | |
| 730 } | |
| 731 | |
| 732 public boolean supportsLinkFlag() { | |
| 733 return walker.supportsLinkFlag(); | |
| 734 } | |
| 673 } | 735 } |
| 674 } | 736 } |
