comparison src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java @ 287:ed6b74a58c66

Use FileInfo abstraction with necessary subset of File functionality instead of File to facilitate other effective file system iterators
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 07 Sep 2011 09:33:27 +0200
parents 6dbbc53fc46d
children 8faad08c709b
comparison
equal deleted inserted replaced
286:954763c82cc3 287:ed6b74a58c66
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 22
23 import java.io.File; 23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.IOException; 24 import java.io.IOException;
26 import java.nio.ByteBuffer; 25 import java.nio.ByteBuffer;
27 import java.nio.channels.FileChannel; 26 import java.nio.channels.ReadableByteChannel;
28 import java.util.ArrayList; 27 import java.util.ArrayList;
29 import java.util.Collections; 28 import java.util.Collections;
30 import java.util.NoSuchElementException; 29 import java.util.NoSuchElementException;
31 import java.util.Set; 30 import java.util.Set;
32 import java.util.TreeSet; 31 import java.util.TreeSet;
39 import org.tmatesoft.hg.internal.FilterByteChannel; 38 import org.tmatesoft.hg.internal.FilterByteChannel;
40 import org.tmatesoft.hg.internal.ManifestRevision; 39 import org.tmatesoft.hg.internal.ManifestRevision;
41 import org.tmatesoft.hg.internal.PathScope; 40 import org.tmatesoft.hg.internal.PathScope;
42 import org.tmatesoft.hg.util.ByteChannel; 41 import org.tmatesoft.hg.util.ByteChannel;
43 import org.tmatesoft.hg.util.CancelledException; 42 import org.tmatesoft.hg.util.CancelledException;
43 import org.tmatesoft.hg.util.FileInfo;
44 import org.tmatesoft.hg.util.FileIterator; 44 import org.tmatesoft.hg.util.FileIterator;
45 import org.tmatesoft.hg.util.FileWalker; 45 import org.tmatesoft.hg.util.FileWalker;
46 import org.tmatesoft.hg.util.Path; 46 import org.tmatesoft.hg.util.Path;
47 import org.tmatesoft.hg.util.PathPool; 47 import org.tmatesoft.hg.util.PathPool;
48 import org.tmatesoft.hg.util.PathRewrite; 48 import org.tmatesoft.hg.util.PathRewrite;
49 import org.tmatesoft.hg.util.RegularFileInfo;
49 50
50 /** 51 /**
51 * 52 *
52 * @author Artem Tikhomirov 53 * @author Artem Tikhomirov
53 * @author TMate Software Ltd. 54 * @author TMate Software Ltd.
160 TreeSet<Path> knownEntries = getDirstate().all(); 161 TreeSet<Path> knownEntries = getDirstate().all();
161 repoWalker.reset(); 162 repoWalker.reset();
162 while (repoWalker.hasNext()) { 163 while (repoWalker.hasNext()) {
163 repoWalker.next(); 164 repoWalker.next();
164 final Path fname = getPathPool().path(repoWalker.name()); 165 final Path fname = getPathPool().path(repoWalker.name());
165 File f = repoWalker.file(); 166 FileInfo f = repoWalker.file();
166 if (!f.exists()) { 167 if (!f.exists()) {
167 // file coming from iterator doesn't exist. 168 // file coming from iterator doesn't exist.
168 if (knownEntries.remove(fname)) { 169 if (knownEntries.remove(fname)) {
169 if (getDirstate().checkRemoved(fname) == null) { 170 if (getDirstate().checkRemoved(fname) == null) {
170 inspector.missing(fname); 171 inspector.missing(fname);
191 inspector.unknown(fname); 192 inspector.unknown(fname);
192 } 193 }
193 } 194 }
194 continue; 195 continue;
195 } 196 }
196 assert f.isFile();
197 if (knownEntries.remove(fname)) { 197 if (knownEntries.remove(fname)) {
198 // tracked file. 198 // tracked file.
199 // modified, added, removed, clean 199 // modified, added, removed, clean
200 if (collect != null) { // need to check against base revision, not FS file 200 if (collect != null) { // need to check against base revision, not FS file
201 checkLocalStatusAgainstBaseRevision(baseRevFiles, collect, baseRevision, fname, f, inspector); 201 checkLocalStatusAgainstBaseRevision(baseRevFiles, collect, baseRevision, fname, f, inspector);
246 } 246 }
247 247
248 //******************************************** 248 //********************************************
249 249
250 250
251 private void checkLocalStatusAgainstFile(Path fname, File f, HgStatusInspector inspector) { 251 private void checkLocalStatusAgainstFile(Path fname, FileInfo f, HgStatusInspector inspector) {
252 HgDirstate.Record r; 252 HgDirstate.Record r;
253 if ((r = getDirstate().checkNormal(fname)) != null) { 253 if ((r = getDirstate().checkNormal(fname)) != null) {
254 // either clean or modified 254 // either clean or modified
255 final boolean timestampEqual = getFileModificationTime(f) == r.time, sizeEqual = r.size == f.length(); 255 final boolean timestampEqual = f.lastModified() == r.time, sizeEqual = r.size == f.length();
256 if (timestampEqual && sizeEqual) { 256 if (timestampEqual && sizeEqual) {
257 inspector.clean(fname); 257 inspector.clean(fname);
258 } else if (!sizeEqual && r.size >= 0) { 258 } else if (!sizeEqual && r.size >= 0) {
259 inspector.modified(fname); 259 inspector.modified(fname);
260 } else { 260 } else {
279 } else if ((r = getDirstate().checkMerged(fname)) != null) { 279 } else if ((r = getDirstate().checkMerged(fname)) != null) {
280 inspector.modified(fname); 280 inspector.modified(fname);
281 } 281 }
282 } 282 }
283 283
284 // return mtime analog, directly comparable to dirstate's mtime.
285 private static int getFileModificationTime(File f) {
286 return (int) (f.lastModified() / 1000);
287 }
288
289 // XXX refactor checkLocalStatus methods in more OO way 284 // XXX refactor checkLocalStatus methods in more OO way
290 private void checkLocalStatusAgainstBaseRevision(Set<Path> baseRevNames, ManifestRevision collect, int baseRevision, Path fname, File f, HgStatusInspector inspector) { 285 private void checkLocalStatusAgainstBaseRevision(Set<Path> baseRevNames, ManifestRevision collect, int baseRevision, Path fname, FileInfo f, HgStatusInspector inspector) {
291 // fname is in the dirstate, either Normal, Added, Removed or Merged 286 // fname is in the dirstate, either Normal, Added, Removed or Merged
292 Nodeid nid1 = collect.nodeid(fname); 287 Nodeid nid1 = collect.nodeid(fname);
293 HgManifest.Flags flags = collect.flags(fname); 288 HgManifest.Flags flags = collect.flags(fname);
294 HgDirstate.Record r; 289 HgDirstate.Record r;
295 if (nid1 == null) { 290 if (nid1 == null) {
323 // was known; check whether clean or modified 318 // was known; check whether clean or modified
324 Nodeid nidFromDirstate = getDirstateParentManifest().nodeid(fname); 319 Nodeid nidFromDirstate = getDirstateParentManifest().nodeid(fname);
325 if ((r = getDirstate().checkNormal(fname)) != null && nid1.equals(nidFromDirstate)) { 320 if ((r = getDirstate().checkNormal(fname)) != null && nid1.equals(nidFromDirstate)) {
326 // regular file, was the same up to WC initialization. Check if was modified since, and, if not, report right away 321 // regular file, was the same up to WC initialization. Check if was modified since, and, if not, report right away
327 // same code as in #checkLocalStatusAgainstFile 322 // same code as in #checkLocalStatusAgainstFile
328 final boolean timestampEqual = getFileModificationTime(f) == r.time, sizeEqual = r.size == f.length(); 323 final boolean timestampEqual = f.lastModified() == r.time, sizeEqual = r.size == f.length();
329 boolean handled = false; 324 boolean handled = false;
330 if (timestampEqual && sizeEqual) { 325 if (timestampEqual && sizeEqual) {
331 inspector.clean(fname); 326 inspector.clean(fname);
332 handled = true; 327 handled = true;
333 } else if (!sizeEqual && r.size >= 0) { 328 } else if (!sizeEqual && r.size >= 0) {
371 // changeset nodeid + hash(actual content) => entry (Nodeid) in the next Manifest 366 // changeset nodeid + hash(actual content) => entry (Nodeid) in the next Manifest
372 // 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). 367 // 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).
373 // The question is whether original Hg treats this case (same content, different parents and hence nodeids) as 'modified' or 'clean' 368 // The question is whether original Hg treats this case (same content, different parents and hence nodeids) as 'modified' or 'clean'
374 } 369 }
375 370
376 private boolean areTheSame(File f, HgDataFile dataFile, Nodeid revision) { 371 private boolean areTheSame(FileInfo f, HgDataFile dataFile, Nodeid revision) {
377 // XXX consider adding HgDataDile.compare(File/byte[]/whatever) operation to optimize comparison 372 // XXX consider adding HgDataDile.compare(File/byte[]/whatever) operation to optimize comparison
378 ByteArrayChannel bac = new ByteArrayChannel(); 373 ByteArrayChannel bac = new ByteArrayChannel();
379 boolean ioFailed = false; 374 boolean ioFailed = false;
380 try { 375 try {
381 int localRevision = dataFile.getLocalRevision(revision); 376 int localRevision = dataFile.getLocalRevision(revision);
388 ioFailed = true; 383 ioFailed = true;
389 } 384 }
390 return !ioFailed && areTheSame(f, bac.toArray(), dataFile.getPath()); 385 return !ioFailed && areTheSame(f, bac.toArray(), dataFile.getPath());
391 } 386 }
392 387
393 private boolean areTheSame(File f, final byte[] data, Path p) { 388 private boolean areTheSame(FileInfo f, final byte[] data, Path p) {
394 FileInputStream fis = null; 389 ReadableByteChannel is = null;
395 try { 390 try {
396 try { 391 try {
397 fis = new FileInputStream(f); 392 is = f.newInputChannel();
398 FileChannel fc = fis.getChannel();
399 ByteBuffer fb = ByteBuffer.allocate(min(1 + data.length * 2 /*to fit couple of lines appended; never zero*/, 8192)); 393 ByteBuffer fb = ByteBuffer.allocate(min(1 + data.length * 2 /*to fit couple of lines appended; never zero*/, 8192));
400 class Check implements ByteChannel { 394 class Check implements ByteChannel {
401 final boolean debug = false; // XXX may want to add global variable to allow clients to turn 395 final boolean debug = false; // XXX may want to add global variable to allow clients to turn
402 boolean sameSoFar = true; 396 boolean sameSoFar = true;
403 int x = 0; 397 int x = 0;
429 return sameSoFar && x == data.length; 423 return sameSoFar && x == data.length;
430 } 424 }
431 }; 425 };
432 Check check = new Check(); 426 Check check = new Check();
433 FilterByteChannel filters = new FilterByteChannel(check, repo.getFiltersFromWorkingDirToRepo(p)); 427 FilterByteChannel filters = new FilterByteChannel(check, repo.getFiltersFromWorkingDirToRepo(p));
434 while (fc.read(fb) != -1 && check.sameSoFar()) { 428 while (is.read(fb) != -1 && check.sameSoFar()) {
435 fb.flip(); 429 fb.flip();
436 filters.write(fb); 430 filters.write(fb);
437 fb.compact(); 431 fb.compact();
438 } 432 }
439 fis.close();
440 return check.ultimatelyTheSame(); 433 return check.ultimatelyTheSame();
441 } catch (IOException ex) { 434 } catch (IOException ex) {
442 if (fis != null) {
443 fis.close();
444 }
445 ex.printStackTrace(); // log warn 435 ex.printStackTrace(); // log warn
436 } finally {
437 if (is != null) {
438 is.close();
439 }
446 } 440 }
447 } catch (/*TODO typed*/Exception ex) { 441 } catch (/*TODO typed*/Exception ex) {
448 ex.printStackTrace(); 442 ex.printStackTrace();
449 } 443 }
450 return false; 444 return false;
451 } 445 }
452 446
453 private static boolean todoCheckFlagsEqual(File f, HgManifest.Flags originalManifestFlags) { 447 private static boolean todoCheckFlagsEqual(FileInfo f, HgManifest.Flags originalManifestFlags) {
454 // FIXME implement 448 // FIXME implement
455 return true; 449 return true;
456 } 450 }
457 451
458 /** 452 /**
511 505
512 private static class FileListIterator implements FileIterator { 506 private static class FileListIterator implements FileIterator {
513 private final File dir; 507 private final File dir;
514 private final Path[] paths; 508 private final Path[] paths;
515 private int index; 509 private int index;
516 private File nextFile; // cache file() in case it's called more than once 510 private RegularFileInfo nextFile;
517 511
518 public FileListIterator(File startDir, Path... files) { 512 public FileListIterator(File startDir, Path... files) {
519 dir = startDir; 513 dir = startDir;
520 paths = files; 514 paths = files;
521 reset(); 515 reset();
522 } 516 }
523 517
524 public void reset() { 518 public void reset() {
525 index = -1; 519 index = -1;
526 nextFile = null; 520 nextFile = new RegularFileInfo();
527 } 521 }
528 522
529 public boolean hasNext() { 523 public boolean hasNext() {
530 return paths.length > 0 && index < paths.length-1; 524 return paths.length > 0 && index < paths.length-1;
531 } 525 }
533 public void next() { 527 public void next() {
534 index++; 528 index++;
535 if (index == paths.length) { 529 if (index == paths.length) {
536 throw new NoSuchElementException(); 530 throw new NoSuchElementException();
537 } 531 }
538 nextFile = new File(dir, paths[index].toString()); 532 nextFile.init(new File(dir, paths[index].toString()));
539 } 533 }
540 534
541 public Path name() { 535 public Path name() {
542 return paths[index]; 536 return paths[index];
543 } 537 }
544 538
545 public File file() { 539 public FileInfo file() {
546 return nextFile; 540 return nextFile;
547 } 541 }
548 542
549 public boolean inScope(Path file) { 543 public boolean inScope(Path file) {
550 for (int i = 0; i < paths.length; i++) { 544 for (int i = 0; i < paths.length; i++) {
595 589
596 public Path name() { 590 public Path name() {
597 return walker.name(); 591 return walker.name();
598 } 592 }
599 593
600 public File file() { 594 public FileInfo file() {
601 return walker.file(); 595 return walker.file();
602 } 596 }
603 597
604 public boolean inScope(Path file) { 598 public boolean inScope(Path file) {
605 return filter.accept(file); 599 return filter.accept(file);