Mercurial > hg4j
comparison src/org/tmatesoft/hg/core/HgLogCommand.java @ 510:90093ee56c0d
Full-fledged test repo to follow file history. Investigating iteration direction alternatives (from new to old in addition to existing old to new)
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Thu, 13 Dec 2012 15:46:40 +0100 |
parents | a30e74dca193 |
children | 122e0600799f |
comparison
equal
deleted
inserted
replaced
509:a30e74dca193 | 510:90093ee56c0d |
---|---|
23 import java.util.Arrays; | 23 import java.util.Arrays; |
24 import java.util.Calendar; | 24 import java.util.Calendar; |
25 import java.util.Collection; | 25 import java.util.Collection; |
26 import java.util.Collections; | 26 import java.util.Collections; |
27 import java.util.ConcurrentModificationException; | 27 import java.util.ConcurrentModificationException; |
28 import java.util.Iterator; | |
28 import java.util.LinkedList; | 29 import java.util.LinkedList; |
29 import java.util.List; | 30 import java.util.List; |
31 import java.util.ListIterator; | |
30 import java.util.Set; | 32 import java.util.Set; |
31 import java.util.TreeSet; | 33 import java.util.TreeSet; |
32 | 34 |
33 import org.tmatesoft.hg.internal.IntMap; | 35 import org.tmatesoft.hg.internal.IntMap; |
34 import org.tmatesoft.hg.internal.IntVector; | 36 import org.tmatesoft.hg.internal.IntVector; |
312 HistoryNode lastFromPrevIteration = null; | 314 HistoryNode lastFromPrevIteration = null; |
313 | 315 |
314 final int CACHE_CSET_IN_ADVANCE_THRESHOLD = 100; /* XXX is it really worth it? */ | 316 final int CACHE_CSET_IN_ADVANCE_THRESHOLD = 100; /* XXX is it really worth it? */ |
315 ElementImpl ei = null; | 317 ElementImpl ei = null; |
316 | 318 |
317 // most recent file is first in the queue | 319 // renamed files in the queue are placed with respect to #iterateDirection |
320 // i.e. if we iterate from new to old, recent filenames come first | |
318 LinkedList<Pair<HgDataFile, Nodeid>> fileRenamesQueue = buildFileRenamesQueue(); | 321 LinkedList<Pair<HgDataFile, Nodeid>> fileRenamesQueue = buildFileRenamesQueue(); |
319 progressHelper.start(4 * fileRenamesQueue.size()); | 322 progressHelper.start(4 * fileRenamesQueue.size()); |
320 do { | 323 do { |
321 // to maintain iteration order from older to newer, take oldest name (in case of renames) first | 324 |
322 Pair<HgDataFile, Nodeid> renameInfo = fileRenamesQueue.removeLast(); | 325 Pair<HgDataFile, Nodeid> renameInfo = fileRenamesQueue.removeFirst(); |
323 cancelHelper.checkCancelled(); | 326 cancelHelper.checkCancelled(); |
324 HgDataFile fileNode = renameInfo.first(); | 327 HgDataFile fileNode = renameInfo.first(); |
325 Nodeid fileLastRevToVisit = null; | 328 Nodeid fileLastRevToVisit = null; |
326 if (followHistory) { | 329 if (followHistory) { |
327 fileLastRevToVisit = renameInfo.second(); | 330 fileLastRevToVisit = renameInfo.second(); |
328 if (fileLastRevToVisit == null) { | 331 if (fileLastRevToVisit == null) { |
329 // only recent file name should not have a changeset when rename has happened. | 332 // it's either first or last item in the queue, depending on iteration order |
330 assert fileRenamesQueue.isEmpty(); | 333 assert fileRenamesQueue.isEmpty() || /*awful way to find out it's first iteration*/ lastFromPrevIteration == null; |
331 // TODO subject to dedicated method either in HgRepository (getWorkingCopyParentRevisionIndex) | 334 // TODO subject to dedicated method either in HgRepository (getWorkingCopyParentRevisionIndex) |
332 // or in the HgDataFile (getWorkingCopyOriginRevision) | 335 // or in the HgDataFile (getWorkingCopyOriginRevision) |
333 Nodeid wdParentChangeset = repo.getWorkingCopyParents().first(); | 336 Nodeid wdParentChangeset = repo.getWorkingCopyParents().first(); |
334 if (!wdParentChangeset.isNull()) { | 337 if (!wdParentChangeset.isNull()) { |
335 int wdParentRevIndex = repo.getChangelog().getRevisionIndex(wdParentChangeset); | 338 int wdParentRevIndex = repo.getChangelog().getRevisionIndex(wdParentChangeset); |
360 } else { | 363 } else { |
361 ph2 = new ProgressSupport.Sub(progressHelper, 3); | 364 ph2 = new ProgressSupport.Sub(progressHelper, 3); |
362 } | 365 } |
363 ph2.start(changeHistory.size()); | 366 ph2.start(changeHistory.size()); |
364 if (lastFromPrevIteration != null) { | 367 if (lastFromPrevIteration != null) { |
365 // forward, from old to new: | 368 if (iterateDirection == IterateDirection.FromOldToNew) { |
366 lastFromPrevIteration.bindChild(changeHistory.get(0)); | 369 // forward, from old to new: |
367 changeHistory.add(0, lastFromPrevIteration); | 370 // A(0..n) -> B(0..m). First, report A(0)..A(n-1) |
371 // then A(n).bind(B(0)) | |
372 HistoryNode oldestOfTheNextChunk = changeHistory.get(0); | |
373 lastFromPrevIteration.bindChild(oldestOfTheNextChunk); | |
374 changeHistory.add(0, lastFromPrevIteration); | |
375 } else { | |
376 assert iterateDirection == IterateDirection.FromNewToOld; | |
377 // A renamed to B. A(0..n) -> B(0..m). | |
378 // First, report B(m), B(m-1)...B(1), then A(n).bind(B(0)) | |
379 HistoryNode newestOfNextChunk = changeHistory.get(changeHistory.size() - 1); // A(n) | |
380 newestOfNextChunk.bindChild(lastFromPrevIteration); | |
381 changeHistory.add(lastFromPrevIteration); | |
382 } | |
368 } | 383 } |
369 if (!fileRenamesQueue.isEmpty()) { | 384 if (!fileRenamesQueue.isEmpty()) { |
370 lastFromPrevIteration = changeHistory.remove(changeHistory.size()-1); | 385 if (iterateDirection == IterateDirection.FromOldToNew) { |
386 // save newest, and exclude it from this iteration (postpone for next) | |
387 lastFromPrevIteration = changeHistory.remove(changeHistory.size()-1); | |
388 } else { | |
389 assert iterateDirection == IterateDirection.FromNewToOld; | |
390 // save oldest, and exclude it from thi iteration (postpone for next) | |
391 lastFromPrevIteration = changeHistory.remove(0); | |
392 } | |
371 } else { | 393 } else { |
372 lastFromPrevIteration = null; // just for the sake of no references to old items | 394 lastFromPrevIteration = null; // just for the sake of no references to old items |
373 } | 395 } |
374 // XXX shall sort changeHistory according to changeset numbers? | 396 // XXX shall sort changeHistory according to changeset numbers? |
375 for (HistoryNode n : changeHistory) { | 397 Iterator<HistoryNode> it; |
398 if (iterateDirection == IterateDirection.FromOldToNew) { | |
399 it = changeHistory.listIterator(); | |
400 } else { | |
401 assert iterateDirection == IterateDirection.FromNewToOld; | |
402 it = new ReverseIterator<HistoryNode>(changeHistory); | |
403 } | |
404 while(it.hasNext()) { | |
405 HistoryNode n = it.next(); | |
376 handler.treeElement(ei.init(n)); | 406 handler.treeElement(ei.init(n)); |
377 ph2.worked(1); | 407 ph2.worked(1); |
378 cancelHelper.checkCancelled(); | 408 cancelHelper.checkCancelled(); |
379 } | 409 } |
380 } while (!fileRenamesQueue.isEmpty()); | 410 } while (!fileRenamesQueue.isEmpty()); |
381 progressHelper.done(); | 411 progressHelper.done(); |
382 } | 412 } |
383 | 413 |
414 private IterateDirection iterateDirection = IterateDirection.FromOldToNew; | |
415 | |
416 private static class ReverseIterator<E> implements Iterator<E> { | |
417 private final ListIterator<E> listIterator; | |
418 | |
419 public ReverseIterator(List<E> list) { | |
420 listIterator = list.listIterator(list.size()); | |
421 } | |
422 | |
423 public boolean hasNext() { | |
424 return listIterator.hasPrevious(); | |
425 } | |
426 public E next() { | |
427 return listIterator.previous(); | |
428 } | |
429 public void remove() { | |
430 listIterator.remove(); | |
431 } | |
432 } | |
433 | |
384 /** | 434 /** |
385 * Follows file renames and build a list of all corresponding file nodes and revisions they were | 435 * Follows file renames and build a list of all corresponding file nodes and revisions they were |
386 * copied/renamed/branched at (IOW, their latest revision to look at). | 436 * copied/renamed/branched at (IOW, their latest revision to look at). |
387 * | 437 * |
388 * If {@link #followHistory} is <code>false</code>, the list contains one element only, | 438 * If {@link #followHistory} is <code>false</code>, the list contains one element only, |
391 * For the most recent file revision is null. | 441 * For the most recent file revision is null. |
392 * | 442 * |
393 * TODO may use HgFileRevision (after some refactoring to accept HgDataFile and Nodeid) instead of Pair | 443 * TODO may use HgFileRevision (after some refactoring to accept HgDataFile and Nodeid) instead of Pair |
394 * and possibly reuse this functionality | 444 * and possibly reuse this functionality |
395 * | 445 * |
396 * @return list of file renames, with most recent file first | 446 * @return list of file renames, ordered with respect to {@link #iterateDirection} |
397 */ | 447 */ |
398 private LinkedList<Pair<HgDataFile, Nodeid>> buildFileRenamesQueue() { | 448 private LinkedList<Pair<HgDataFile, Nodeid>> buildFileRenamesQueue() { |
399 LinkedList<Pair<HgDataFile, Nodeid>> rv = new LinkedList<Pair<HgDataFile, Nodeid>>(); | 449 LinkedList<Pair<HgDataFile, Nodeid>> rv = new LinkedList<Pair<HgDataFile, Nodeid>>(); |
400 if (!followHistory) { | 450 if (!followHistory) { |
401 rv.add(new Pair<HgDataFile, Nodeid>(repo.getFileNode(file), null)); | 451 rv.add(new Pair<HgDataFile, Nodeid>(repo.getFileNode(file), null)); |
404 Path fp = file; | 454 Path fp = file; |
405 Nodeid copyRev = null; | 455 Nodeid copyRev = null; |
406 boolean isCopy; | 456 boolean isCopy; |
407 do { | 457 do { |
408 HgDataFile fileNode = repo.getFileNode(fp); | 458 HgDataFile fileNode = repo.getFileNode(fp); |
409 rv.addLast(new Pair<HgDataFile, Nodeid>(fileNode, copyRev)); | 459 Pair<HgDataFile, Nodeid> p = new Pair<HgDataFile, Nodeid>(fileNode, copyRev); |
460 if (iterateDirection == IterateDirection.FromOldToNew) { | |
461 rv.addFirst(p); | |
462 } else { | |
463 assert iterateDirection == IterateDirection.FromNewToOld; | |
464 rv.addLast(p); | |
465 } | |
410 if (isCopy = fileNode.isCopy()) { | 466 if (isCopy = fileNode.isCopy()) { |
411 fp = fileNode.getCopySourceName(); | 467 fp = fileNode.getCopySourceName(); |
412 copyRev = fileNode.getCopySourceRevision(); | 468 copyRev = fileNode.getCopySourceRevision(); |
413 } | 469 } |
414 } while (isCopy); | 470 } while (isCopy); |
792 } else { | 848 } else { |
793 return repo.getChangelog().getRevision(changelogRevisionNumber); | 849 return repo.getChangelog().getRevision(changelogRevisionNumber); |
794 } | 850 } |
795 } | 851 } |
796 } | 852 } |
853 | |
854 private enum IterateDirection { | |
855 FromOldToNew, FromNewToOld | |
856 } | |
797 } | 857 } |