Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java @ 348:a0864b2892cd
Expose errors reading mercurial control files with exception
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> | 
|---|---|
| date | Thu, 24 Nov 2011 02:57:03 +0100 | 
| parents | f377f833b780 | 
| children | 33eaed1ad130 | 
   comparison
  equal
  deleted
  inserted
  replaced
| 347:8da7ade36c57 | 348:a0864b2892cd | 
|---|---|
| 31 import java.util.TreeSet; | 31 import java.util.TreeSet; | 
| 32 | 32 | 
| 33 import org.tmatesoft.hg.core.HgBadStateException; | 33 import org.tmatesoft.hg.core.HgBadStateException; | 
| 34 import org.tmatesoft.hg.core.HgDataStreamException; | 34 import org.tmatesoft.hg.core.HgDataStreamException; | 
| 35 import org.tmatesoft.hg.core.HgException; | 35 import org.tmatesoft.hg.core.HgException; | 
| 36 import org.tmatesoft.hg.core.HgInvalidControlFileException; | |
| 36 import org.tmatesoft.hg.core.Nodeid; | 37 import org.tmatesoft.hg.core.Nodeid; | 
| 37 import org.tmatesoft.hg.internal.ByteArrayChannel; | 38 import org.tmatesoft.hg.internal.ByteArrayChannel; | 
| 38 import org.tmatesoft.hg.internal.Experimental; | 39 import org.tmatesoft.hg.internal.Experimental; | 
| 39 import org.tmatesoft.hg.internal.FilterByteChannel; | 40 import org.tmatesoft.hg.internal.FilterByteChannel; | 
| 40 import org.tmatesoft.hg.internal.ManifestRevision; | 41 import org.tmatesoft.hg.internal.ManifestRevision; | 
| 98 | 99 | 
| 99 /** | 100 /** | 
| 100 * Access to directory state information this collector uses. | 101 * Access to directory state information this collector uses. | 
| 101 * @return directory state holder, never <code>null</code> | 102 * @return directory state holder, never <code>null</code> | 
| 102 */ | 103 */ | 
| 103 public HgDirstate getDirstate() { | 104 public HgDirstate getDirstate() throws HgInvalidControlFileException { | 
| 104 if (dirstate == null) { | 105 if (dirstate == null) { | 
| 105 dirstate = repo.loadDirstate(getPathPool()); | 106 dirstate = repo.loadDirstate(getPathPool()); | 
| 106 } | 107 } | 
| 108 return dirstate; | |
| 109 } | |
| 110 | |
| 111 private HgDirstate getDirstateImpl() { | |
| 107 return dirstate; | 112 return dirstate; | 
| 108 } | 113 } | 
| 109 | 114 | 
| 110 private ManifestRevision getManifest(int changelogLocalRev) { | 115 private ManifestRevision getManifest(int changelogLocalRev) { | 
| 111 assert changelogLocalRev >= 0; | 116 assert changelogLocalRev >= 0; | 
| 121 | 126 | 
| 122 private ManifestRevision getDirstateParentManifest() { | 127 private ManifestRevision getDirstateParentManifest() { | 
| 123 // WC not necessarily points to TIP, but may be result of update to any previous revision. | 128 // WC not necessarily points to TIP, but may be result of update to any previous revision. | 
| 124 // In such case, we need to compare local files not to their TIP content, but to specific version at the time of selected revision | 129 // In such case, we need to compare local files not to their TIP content, but to specific version at the time of selected revision | 
| 125 if (dirstateParentManifest == null) { | 130 if (dirstateParentManifest == null) { | 
| 126 Nodeid dirstateParent = getDirstate().parents().first(); | 131 Nodeid dirstateParent = getDirstateImpl().parents().first(); | 
| 127 if (dirstateParent.isNull()) { | 132 if (dirstateParent.isNull()) { | 
| 128 dirstateParentManifest = baseRevisionCollector != null ? baseRevisionCollector.raw(-1) : HgStatusCollector.createEmptyManifestRevision(); | 133 dirstateParentManifest = baseRevisionCollector != null ? baseRevisionCollector.raw(-1) : HgStatusCollector.createEmptyManifestRevision(); | 
| 129 } else { | 134 } else { | 
| 130 int changelogLocalRev = repo.getChangelog().getLocalRevision(dirstateParent); | 135 int changelogLocalRev = repo.getChangelog().getLocalRevision(dirstateParent); | 
| 131 dirstateParentManifest = getManifest(changelogLocalRev); | 136 dirstateParentManifest = getManifest(changelogLocalRev); | 
| 137 // may be invoked few times, TIP or WORKING_COPY indicate comparison shall be run against working copy parent | 142 // may be invoked few times, TIP or WORKING_COPY indicate comparison shall be run against working copy parent | 
| 138 // NOTE, use of TIP constant requires certain care. TIP here doesn't mean latest cset, but actual working copy parent. | 143 // NOTE, use of TIP constant requires certain care. TIP here doesn't mean latest cset, but actual working copy parent. | 
| 139 public void walk(int baseRevision, HgStatusInspector inspector) { | 144 public void walk(int baseRevision, HgStatusInspector inspector) { | 
| 140 if (HgInternals.wrongLocalRevision(baseRevision) || baseRevision == BAD_REVISION) { | 145 if (HgInternals.wrongLocalRevision(baseRevision) || baseRevision == BAD_REVISION) { | 
| 141 throw new IllegalArgumentException(String.valueOf(baseRevision)); | 146 throw new IllegalArgumentException(String.valueOf(baseRevision)); | 
| 147 } | |
| 148 if (getDirstateImpl() == null) { | |
| 149 // XXX this is a hack to avoid declaring throws for the #walk() at the moment | |
| 150 // once I decide whether to have mediator that collects errors or to use exceptions here | |
| 151 // this hack shall be removed in favor of either severe error in mediator or a re-thrown exception. | |
| 152 try { | |
| 153 getDirstate(); | |
| 154 } catch (HgInvalidControlFileException ex) { | |
| 155 repo.getContext().getLog().error(getClass(), ex, "Can't read dirstate"); | |
| 156 return; | |
| 157 } | |
| 142 } | 158 } | 
| 143 ManifestRevision collect = null; // non null indicates we compare against base revision | 159 ManifestRevision collect = null; // non null indicates we compare against base revision | 
| 144 Set<Path> baseRevFiles = Collections.emptySet(); // files from base revision not affected by status calculation | 160 Set<Path> baseRevFiles = Collections.emptySet(); // files from base revision not affected by status calculation | 
| 145 if (baseRevision != TIP && baseRevision != WORKING_COPY) { | 161 if (baseRevision != TIP && baseRevision != WORKING_COPY) { | 
| 146 collect = getManifest(baseRevision); | 162 collect = getManifest(baseRevision); | 
| 162 ((HgStatusCollector.Record) inspector).init(rev1, rev2, sc); | 178 ((HgStatusCollector.Record) inspector).init(rev1, rev2, sc); | 
| 163 } | 179 } | 
| 164 final HgIgnore hgIgnore = repo.getIgnore(); | 180 final HgIgnore hgIgnore = repo.getIgnore(); | 
| 165 repoWalker.reset(); | 181 repoWalker.reset(); | 
| 166 TreeSet<Path> processed = new TreeSet<Path>(); // names of files we handled as they known to Dirstate (not FileIterator) | 182 TreeSet<Path> processed = new TreeSet<Path>(); // names of files we handled as they known to Dirstate (not FileIterator) | 
| 167 final HgDirstate ds = getDirstate(); | 183 final HgDirstate ds = getDirstateImpl(); | 
| 168 TreeSet<Path> knownEntries = ds.all(); // here just to get dirstate initialized | 184 TreeSet<Path> knownEntries = ds.all(); // here just to get dirstate initialized | 
| 169 while (repoWalker.hasNext()) { | 185 while (repoWalker.hasNext()) { | 
| 170 repoWalker.next(); | 186 repoWalker.next(); | 
| 171 final Path fname = getPathPool().path(repoWalker.name()); | 187 final Path fname = getPathPool().path(repoWalker.name()); | 
| 172 FileInfo f = repoWalker.file(); | 188 FileInfo f = repoWalker.file(); | 
| 259 //******************************************** | 275 //******************************************** | 
| 260 | 276 | 
| 261 | 277 | 
| 262 private void checkLocalStatusAgainstFile(Path fname, FileInfo f, HgStatusInspector inspector) { | 278 private void checkLocalStatusAgainstFile(Path fname, FileInfo f, HgStatusInspector inspector) { | 
| 263 HgDirstate.Record r; | 279 HgDirstate.Record r; | 
| 264 if ((r = getDirstate().checkNormal(fname)) != null) { | 280 if ((r = getDirstateImpl().checkNormal(fname)) != null) { | 
| 265 // either clean or modified | 281 // either clean or modified | 
| 266 final boolean timestampEqual = f.lastModified() == r.modificationTime(), sizeEqual = r.size() == f.length(); | 282 final boolean timestampEqual = f.lastModified() == r.modificationTime(), sizeEqual = r.size() == f.length(); | 
| 267 if (timestampEqual && sizeEqual) { | 283 if (timestampEqual && sizeEqual) { | 
| 268 inspector.clean(fname); | 284 inspector.clean(fname); | 
| 269 } else if (!sizeEqual && r.size() >= 0) { | 285 } else if (!sizeEqual && r.size() >= 0) { | 
| 285 inspector.modified(df.getPath()); | 301 inspector.modified(df.getPath()); | 
| 286 } else { | 302 } else { | 
| 287 inspector.clean(df.getPath()); | 303 inspector.clean(df.getPath()); | 
| 288 } | 304 } | 
| 289 } | 305 } | 
| 290 } else if ((r = getDirstate().checkAdded(fname)) != null) { | 306 } else if ((r = getDirstateImpl().checkAdded(fname)) != null) { | 
| 291 if (r.copySource() == null) { | 307 if (r.copySource() == null) { | 
| 292 inspector.added(fname); | 308 inspector.added(fname); | 
| 293 } else { | 309 } else { | 
| 294 inspector.copied(r.copySource(), fname); | 310 inspector.copied(r.copySource(), fname); | 
| 295 } | 311 } | 
| 296 } else if ((r = getDirstate().checkRemoved(fname)) != null) { | 312 } else if ((r = getDirstateImpl().checkRemoved(fname)) != null) { | 
| 297 inspector.removed(fname); | 313 inspector.removed(fname); | 
| 298 } else if ((r = getDirstate().checkMerged(fname)) != null) { | 314 } else if ((r = getDirstateImpl().checkMerged(fname)) != null) { | 
| 299 inspector.modified(fname); | 315 inspector.modified(fname); | 
| 300 } | 316 } | 
| 301 } | 317 } | 
| 302 | 318 | 
| 303 // XXX refactor checkLocalStatus methods in more OO way | 319 // XXX refactor checkLocalStatus methods in more OO way | 
| 308 HgDirstate.Record r; | 324 HgDirstate.Record r; | 
| 309 if (nid1 == null) { | 325 if (nid1 == null) { | 
| 310 // normal: added? | 326 // normal: added? | 
| 311 // added: not known at the time of baseRevision, shall report | 327 // added: not known at the time of baseRevision, shall report | 
| 312 // merged: was not known, report as added? | 328 // merged: was not known, report as added? | 
| 313 if ((r = getDirstate().checkNormal(fname)) != null) { | 329 if ((r = getDirstateImpl().checkNormal(fname)) != null) { | 
| 314 try { | 330 try { | 
| 315 Path origin = HgStatusCollector.getOriginIfCopy(repo, fname, baseRevNames, baseRevision); | 331 Path origin = HgStatusCollector.getOriginIfCopy(repo, fname, baseRevNames, baseRevision); | 
| 316 if (origin != null) { | 332 if (origin != null) { | 
| 317 inspector.copied(getPathPool().path(origin), fname); | 333 inspector.copied(getPathPool().path(origin), fname); | 
| 318 return; | 334 return; | 
| 319 } | 335 } | 
| 320 } catch (HgDataStreamException ex) { | 336 } catch (HgDataStreamException ex) { | 
| 321 ex.printStackTrace(); | 337 ex.printStackTrace(); | 
| 322 // FIXME report to a mediator, continue status collection | 338 // FIXME report to a mediator, continue status collection | 
| 323 } | 339 } | 
| 324 } else if ((r = getDirstate().checkAdded(fname)) != null) { | 340 } else if ((r = getDirstateImpl().checkAdded(fname)) != null) { | 
| 325 if (r.copySource() != null && baseRevNames.contains(r.copySource())) { | 341 if (r.copySource() != null && baseRevNames.contains(r.copySource())) { | 
| 326 baseRevNames.remove(r.copySource()); // XXX surely I shall not report rename source as Removed? | 342 baseRevNames.remove(r.copySource()); // XXX surely I shall not report rename source as Removed? | 
| 327 inspector.copied(r.copySource(), fname); | 343 inspector.copied(r.copySource(), fname); | 
| 328 return; | 344 return; | 
| 329 } | 345 } | 
| 330 // fall-through, report as added | 346 // fall-through, report as added | 
| 331 } else if (getDirstate().checkRemoved(fname) != null) { | 347 } else if (getDirstateImpl().checkRemoved(fname) != null) { | 
| 332 // removed: removed file was not known at the time of baseRevision, and we should not report it as removed | 348 // removed: removed file was not known at the time of baseRevision, and we should not report it as removed | 
| 333 return; | 349 return; | 
| 334 } | 350 } | 
| 335 inspector.added(fname); | 351 inspector.added(fname); | 
| 336 } else { | 352 } else { | 
| 337 // was known; check whether clean or modified | 353 // was known; check whether clean or modified | 
| 338 Nodeid nidFromDirstate = getDirstateParentManifest().nodeid(fname); | 354 Nodeid nidFromDirstate = getDirstateParentManifest().nodeid(fname); | 
| 339 if ((r = getDirstate().checkNormal(fname)) != null && nid1.equals(nidFromDirstate)) { | 355 if ((r = getDirstateImpl().checkNormal(fname)) != null && nid1.equals(nidFromDirstate)) { | 
| 340 // regular file, was the same up to WC initialization. Check if was modified since, and, if not, report right away | 356 // regular file, was the same up to WC initialization. Check if was modified since, and, if not, report right away | 
| 341 // same code as in #checkLocalStatusAgainstFile | 357 // same code as in #checkLocalStatusAgainstFile | 
| 342 final boolean timestampEqual = f.lastModified() == r.modificationTime(), sizeEqual = r.size() == f.length(); | 358 final boolean timestampEqual = f.lastModified() == r.modificationTime(), sizeEqual = r.size() == f.length(); | 
| 343 boolean handled = false; | 359 boolean handled = false; | 
| 344 if (timestampEqual && sizeEqual) { | 360 if (timestampEqual && sizeEqual) { | 
| 358 } | 374 } | 
| 359 // otherwise, shall check actual content (size not the same, or unknown (-1 or -2), or timestamp is different, | 375 // otherwise, shall check actual content (size not the same, or unknown (-1 or -2), or timestamp is different, | 
| 360 // or nodeid in dirstate is different, but local change might have brought it back to baseRevision state) | 376 // or nodeid in dirstate is different, but local change might have brought it back to baseRevision state) | 
| 361 // FALL THROUGH | 377 // FALL THROUGH | 
| 362 } | 378 } | 
| 363 if (r != null || (r = getDirstate().checkMerged(fname)) != null || (r = getDirstate().checkAdded(fname)) != null) { | 379 if (r != null || (r = getDirstateImpl().checkMerged(fname)) != null || (r = getDirstateImpl().checkAdded(fname)) != null) { | 
| 364 // check actual content to see actual changes | 380 // check actual content to see actual changes | 
| 365 // when added - seems to be the case of a file added once again, hence need to check if content is different | 381 // when added - seems to be the case of a file added once again, hence need to check if content is different | 
| 366 // either clean or modified | 382 // either clean or modified | 
| 367 HgDataFile fileNode = repo.getFileNode(fname); | 383 HgDataFile fileNode = repo.getFileNode(fname); | 
| 368 if (areTheSame(f, fileNode, nid1)) { | 384 if (areTheSame(f, fileNode, nid1)) { | 
| 369 inspector.clean(fname); | 385 inspector.clean(fname); | 
| 370 } else { | 386 } else { | 
| 371 inspector.modified(fname); | 387 inspector.modified(fname); | 
| 372 } | 388 } | 
| 373 baseRevNames.remove(fname); // consumed, processed, handled. | 389 baseRevNames.remove(fname); // consumed, processed, handled. | 
| 374 } else if (getDirstate().checkRemoved(fname) != null) { | 390 } else if (getDirstateImpl().checkRemoved(fname) != null) { | 
| 375 // was known, and now marked as removed, report it right away, do not rely on baseRevNames processing later | 391 // was known, and now marked as removed, report it right away, do not rely on baseRevNames processing later | 
| 376 inspector.removed(fname); | 392 inspector.removed(fname); | 
| 377 baseRevNames.remove(fname); // consumed, processed, handled. | 393 baseRevNames.remove(fname); // consumed, processed, handled. | 
| 378 } | 394 } | 
| 379 // only those left in baseRevNames after processing are reported as removed | 395 // only those left in baseRevNames after processing are reported as removed | 
