Mercurial > hg4j
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); |