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 }