Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java @ 229:1ec6b327a6ac
Scope for status reworked: explicit files or a general matcher
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Tue, 31 May 2011 05:23:07 +0200 |
| parents | 26ad7827a62d |
| children | 6e1373b54e9b |
comparison
equal
deleted
inserted
replaced
| 228:fffe4f882248 | 229:1ec6b327a6ac |
|---|---|
| 17 package org.tmatesoft.hg.repo; | 17 package org.tmatesoft.hg.repo; |
| 18 | 18 |
| 19 import static java.lang.Math.max; | 19 import static java.lang.Math.max; |
| 20 import static java.lang.Math.min; | 20 import static java.lang.Math.min; |
| 21 import static org.tmatesoft.hg.repo.HgRepository.*; | 21 import static org.tmatesoft.hg.repo.HgRepository.*; |
| 22 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; | |
| 23 import static org.tmatesoft.hg.repo.HgRepository.TIP; | |
| 24 | 22 |
| 25 import java.io.File; | 23 import java.io.File; |
| 26 import java.io.FileInputStream; | 24 import java.io.FileInputStream; |
| 27 import java.io.IOException; | 25 import java.io.IOException; |
| 28 import java.nio.ByteBuffer; | 26 import java.nio.ByteBuffer; |
| 29 import java.nio.channels.FileChannel; | 27 import java.nio.channels.FileChannel; |
| 28 import java.util.ArrayList; | |
| 30 import java.util.Collections; | 29 import java.util.Collections; |
| 31 import java.util.NoSuchElementException; | 30 import java.util.NoSuchElementException; |
| 32 import java.util.Set; | 31 import java.util.Set; |
| 33 import java.util.TreeSet; | 32 import java.util.TreeSet; |
| 34 | 33 |
| 36 import org.tmatesoft.hg.core.HgException; | 35 import org.tmatesoft.hg.core.HgException; |
| 37 import org.tmatesoft.hg.core.Nodeid; | 36 import org.tmatesoft.hg.core.Nodeid; |
| 38 import org.tmatesoft.hg.internal.ByteArrayChannel; | 37 import org.tmatesoft.hg.internal.ByteArrayChannel; |
| 39 import org.tmatesoft.hg.internal.Experimental; | 38 import org.tmatesoft.hg.internal.Experimental; |
| 40 import org.tmatesoft.hg.internal.FilterByteChannel; | 39 import org.tmatesoft.hg.internal.FilterByteChannel; |
| 41 import org.tmatesoft.hg.internal.RelativePathRewrite; | 40 import org.tmatesoft.hg.internal.PathScope; |
| 42 import org.tmatesoft.hg.repo.HgStatusCollector.ManifestRevisionInspector; | 41 import org.tmatesoft.hg.repo.HgStatusCollector.ManifestRevisionInspector; |
| 43 import org.tmatesoft.hg.util.ByteChannel; | 42 import org.tmatesoft.hg.util.ByteChannel; |
| 44 import org.tmatesoft.hg.util.CancelledException; | 43 import org.tmatesoft.hg.util.CancelledException; |
| 45 import org.tmatesoft.hg.util.FileIterator; | 44 import org.tmatesoft.hg.util.FileIterator; |
| 46 import org.tmatesoft.hg.util.FileWalker; | 45 import org.tmatesoft.hg.util.FileWalker; |
| 60 private HgDirstate dirstate; | 59 private HgDirstate dirstate; |
| 61 private HgStatusCollector baseRevisionCollector; | 60 private HgStatusCollector baseRevisionCollector; |
| 62 private PathPool pathPool; | 61 private PathPool pathPool; |
| 63 | 62 |
| 64 public HgWorkingCopyStatusCollector(HgRepository hgRepo) { | 63 public HgWorkingCopyStatusCollector(HgRepository hgRepo) { |
| 65 this(hgRepo, hgRepo.createWorkingDirWalker()); | 64 this(hgRepo, new HgInternals(hgRepo).createWorkingDirWalker(null)); |
| 66 } | 65 } |
| 67 | 66 |
| 68 HgWorkingCopyStatusCollector(HgRepository hgRepo, FileIterator hgRepoWalker) { | 67 // FIXME document cons |
| 68 public HgWorkingCopyStatusCollector(HgRepository hgRepo, FileIterator hgRepoWalker) { | |
| 69 repo = hgRepo; | 69 repo = hgRepo; |
| 70 repoWalker = hgRepoWalker; | 70 repoWalker = hgRepoWalker; |
| 71 } | 71 } |
| 72 | 72 |
| 73 /** | 73 /** |
| 134 final PathPool pp = getPathPool(); | 134 final PathPool pp = getPathPool(); |
| 135 while (repoWalker.hasNext()) { | 135 while (repoWalker.hasNext()) { |
| 136 repoWalker.next(); | 136 repoWalker.next(); |
| 137 Path fname = pp.path(repoWalker.name()); | 137 Path fname = pp.path(repoWalker.name()); |
| 138 File f = repoWalker.file(); | 138 File f = repoWalker.file(); |
| 139 assert f.isFile(); | |
| 140 if (!f.exists()) { | 139 if (!f.exists()) { |
| 141 // file coming from iterator doesn't exist. | 140 // file coming from iterator doesn't exist. |
| 142 if (knownEntries.remove(fname.toString())) { | 141 if (knownEntries.remove(fname.toString())) { |
| 143 if (getDirstate().checkRemoved(fname) == null) { | 142 if (getDirstate().checkRemoved(fname) == null) { |
| 144 inspector.missing(fname); | 143 inspector.missing(fname); |
| 165 inspector.unknown(fname); | 164 inspector.unknown(fname); |
| 166 } | 165 } |
| 167 } | 166 } |
| 168 continue; | 167 continue; |
| 169 } | 168 } |
| 169 assert f.isFile(); | |
| 170 if (knownEntries.remove(fname.toString())) { | 170 if (knownEntries.remove(fname.toString())) { |
| 171 // tracked file. | 171 // tracked file. |
| 172 // modified, added, removed, clean | 172 // modified, added, removed, clean |
| 173 if (collect != null) { // need to check against base revision, not FS file | 173 if (collect != null) { // need to check against base revision, not FS file |
| 174 checkLocalStatusAgainstBaseRevision(baseRevFiles, collect, baseRevision, fname, f, inspector); | 174 checkLocalStatusAgainstBaseRevision(baseRevFiles, collect, baseRevision, fname, f, inspector); |
| 397 private static String todoGenerateFlags(Path fname) { | 397 private static String todoGenerateFlags(Path fname) { |
| 398 // FIXME implement | 398 // FIXME implement |
| 399 return null; | 399 return null; |
| 400 } | 400 } |
| 401 | 401 |
| 402 @Experimental(reason="There's intention to support status query with multiple files/dirs, API might get changed") | 402 /** |
| 403 public static HgWorkingCopyStatusCollector create(HgRepository hgRepo, Path file) { | 403 * Configure status collector to consider only subset of a working copy tree. Tries to be as effective as possible, and to |
| 404 FileIterator fi = file.isDirectory() ? new DirFileIterator(hgRepo, file) : new FileListIterator(hgRepo.getRepositoryRoot().getParentFile(), file); | 404 * traverse only relevant part of working copy on the filesystem. |
| 405 * | |
| 406 * @param hgRepo repository | |
| 407 * @param paths repository-relative files and/or directories. Directories are processed recursively. | |
| 408 * | |
| 409 * @return new instance of {@link HgWorkingCopyStatusCollector}, ready to {@link #walk(int, HgStatusInspector) walk} associated working copy | |
| 410 */ | |
| 411 @Experimental(reason="Provisional API") | |
| 412 public static HgWorkingCopyStatusCollector create(HgRepository hgRepo, Path... paths) { | |
| 413 ArrayList<Path> f = new ArrayList<Path>(5); | |
| 414 ArrayList<Path> d = new ArrayList<Path>(5); | |
| 415 for (Path p : paths) { | |
| 416 if (p.isDirectory()) { | |
| 417 d.add(p); | |
| 418 } else { | |
| 419 f.add(p); | |
| 420 } | |
| 421 } | |
| 422 // final Path[] dirs = f.toArray(new Path[d.size()]); | |
| 423 if (d.isEmpty()) { | |
| 424 final Path[] files = f.toArray(new Path[f.size()]); | |
| 425 FileIterator fi = new FileListIterator(hgRepo.getRepositoryRoot().getParentFile(), files); | |
| 426 return new HgWorkingCopyStatusCollector(hgRepo, fi); | |
| 427 } | |
| 428 // | |
| 429 | |
| 430 //FileIterator fi = file.isDirectory() ? new DirFileIterator(hgRepo, file) : new FileListIterator(, file); | |
| 431 FileIterator fi = new HgInternals(hgRepo).createWorkingDirWalker(new PathScope(true, paths)); | |
| 405 return new HgWorkingCopyStatusCollector(hgRepo, fi); | 432 return new HgWorkingCopyStatusCollector(hgRepo, fi); |
| 433 } | |
| 434 | |
| 435 /** | |
| 436 * Configure collector object to calculate status for matching files only. | |
| 437 * This method may be less effective than explicit list of files as it iterates over whole repository | |
| 438 * (thus supplied matcher doesn't need to care if directories to files in question are also in scope, | |
| 439 * see {@link FileWalker#FileWalker(File, Path.Source, Path.Matcher)}) | |
| 440 * | |
| 441 * @return new instance of {@link HgWorkingCopyStatusCollector}, ready to {@link #walk(int, HgStatusInspector) walk} associated working copy | |
| 442 */ | |
| 443 @Experimental(reason="Provisional API. May add boolean strict argument for those who write smart matchers that can be used in FileWalker") | |
| 444 public static HgWorkingCopyStatusCollector create(HgRepository hgRepo, Path.Matcher scope) { | |
| 445 FileIterator w = new HgInternals(hgRepo).createWorkingDirWalker(null); | |
| 446 FileIterator wf = (scope == null || scope instanceof Path.Matcher.Any) ? w : new FileIteratorFilter(w, scope); | |
| 447 // the reason I need to iterate over full repo and apply filter is that I have no idea whatsoever about | |
| 448 // patterns in the scope. I.e. if scope lists a file (PathGlobMatcher("a/b/c.txt")), FileWalker won't get deep | |
| 449 // to the file unless matcher would also explicitly include "a/", "a/b/" in scope. Since I can't rely | |
| 450 // users would write robust matchers, and I don't see a decent way to enforce that (i.e. factory to produce | |
| 451 // correct matcher from Path is much like what PathScope does, and can be accessed directly with #create(repo, Path...) | |
| 452 // method above/ | |
| 453 return new HgWorkingCopyStatusCollector(hgRepo, wf); | |
| 406 } | 454 } |
| 407 | 455 |
| 408 private static class FileListIterator implements FileIterator { | 456 private static class FileListIterator implements FileIterator { |
| 409 private final File dir; | 457 private final File dir; |
| 410 private final Path[] paths; | 458 private final Path[] paths; |
| 450 } | 498 } |
| 451 return false; | 499 return false; |
| 452 } | 500 } |
| 453 } | 501 } |
| 454 | 502 |
| 455 private static class DirFileIterator implements FileIterator { | 503 private static class FileIteratorFilter implements FileIterator { |
| 456 private final Path dirOfInterest; | 504 private final Path.Matcher filter; |
| 457 private final FileWalker walker; | 505 private final FileIterator walker; |
| 458 | 506 private boolean didNext = false; |
| 459 public DirFileIterator(HgRepository hgRepo, Path directory) { | 507 |
| 460 dirOfInterest = directory; | 508 public FileIteratorFilter(FileIterator fileWalker, Path.Matcher filterMatcher) { |
| 461 File dir = hgRepo.getRepositoryRoot().getParentFile(); | 509 assert fileWalker != null; |
| 462 Path.Source pathSrc = new Path.SimpleSource(new PathRewrite.Composite(new RelativePathRewrite(dir), hgRepo.getToRepoPathHelper())); | 510 assert filterMatcher != null; |
| 463 walker = new FileWalker(new File(dir, directory.toString()), pathSrc); | 511 filter = filterMatcher; |
| 512 walker = fileWalker; | |
| 464 } | 513 } |
| 465 | 514 |
| 466 public void reset() { | 515 public void reset() { |
| 467 walker.reset(); | 516 walker.reset(); |
| 468 } | 517 } |
| 469 | 518 |
| 470 public boolean hasNext() { | 519 public boolean hasNext() { |
| 471 return walker.hasNext(); | 520 while (walker.hasNext()) { |
| 521 walker.next(); | |
| 522 if (filter.accept(walker.name())) { | |
| 523 didNext = true; | |
| 524 return true; | |
| 525 } | |
| 526 } | |
| 527 return false; | |
| 472 } | 528 } |
| 473 | 529 |
| 474 public void next() { | 530 public void next() { |
| 475 walker.next(); | 531 if (didNext) { |
| 532 didNext = false; | |
| 533 } else { | |
| 534 if (!hasNext()) { | |
| 535 throw new NoSuchElementException(); | |
| 536 } | |
| 537 } | |
| 476 } | 538 } |
| 477 | 539 |
| 478 public Path name() { | 540 public Path name() { |
| 479 return walker.name(); | 541 return walker.name(); |
| 480 } | 542 } |
| 482 public File file() { | 544 public File file() { |
| 483 return walker.file(); | 545 return walker.file(); |
| 484 } | 546 } |
| 485 | 547 |
| 486 public boolean inScope(Path file) { | 548 public boolean inScope(Path file) { |
| 487 return file.toString().startsWith(dirOfInterest.toString()); | 549 return filter.accept(file); |
| 488 } | 550 } |
| 489 } | 551 } |
| 490 } | 552 } |
