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 } |