comparison src/org/tmatesoft/hg/repo/HgBlameFacility.java @ 573:e49f9d9513fa

Partial blame when start/end revisions are in the middle of a single filename history
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 12 Apr 2013 19:50:21 +0200
parents 36853bb80a35
children 43cfa08ff3fd
comparison
equal deleted inserted replaced
572:becd2a1310a2 573:e49f9d9513fa
77 */ 77 */
78 public void annotate(int changelogRevIndexStart, int changelogRevIndexEnd, Inspector insp, HgIterateDirection iterateOrder) throws HgCallbackTargetException { 78 public void annotate(int changelogRevIndexStart, int changelogRevIndexEnd, Inspector insp, HgIterateDirection iterateOrder) throws HgCallbackTargetException {
79 if (wrongRevisionIndex(changelogRevIndexStart) || wrongRevisionIndex(changelogRevIndexEnd)) { 79 if (wrongRevisionIndex(changelogRevIndexStart) || wrongRevisionIndex(changelogRevIndexEnd)) {
80 throw new IllegalArgumentException(); 80 throw new IllegalArgumentException();
81 } 81 }
82 // Note, changelogRevisionIndex may be TIP, while the code below doesn't tolerate constants 82 // Note, changelogRevIndexEnd may be TIP, while the code below doesn't tolerate constants
83 // 83 //
84 int lastRevision = df.getRepo().getChangelog().getLastRevision(); 84 int lastRevision = df.getRepo().getChangelog().getLastRevision();
85 if (changelogRevIndexEnd == TIP) { 85 if (changelogRevIndexEnd == TIP) {
86 changelogRevIndexEnd = lastRevision; 86 changelogRevIndexEnd = lastRevision;
87 } 87 }
99 fileHistory.init(fileLastClogRevIndex); 99 fileHistory.init(fileLastClogRevIndex);
100 fileHistory.linkTo(nextChunk); 100 fileHistory.linkTo(nextChunk);
101 fileCompleteHistory.addFirst(fileHistory); // to get the list in old-to-new order 101 fileCompleteHistory.addFirst(fileHistory); // to get the list in old-to-new order
102 nextChunk = fileHistory; 102 nextChunk = fileHistory;
103 bh.useFileUpTo(currentFile, fileLastClogRevIndex); 103 bh.useFileUpTo(currentFile, fileLastClogRevIndex);
104 if (currentFile.isCopy()) { 104 if (fileHistory.changeset(0) > changelogRevIndexStart && currentFile.isCopy()) {
105 // TODO SessionContext.getPathFactory() and replace all Path.create 105 // fileHistory.changeset(0) is the earliest revision we know about so far,
106 // once we get to revisions earlier than the requested start, stop digging.
107 // The reason there's NO == (i.e. not >=) because:
108 // (easy): once it's equal, we've reached our intended start
109 // (hard): if changelogRevIndexStart happens to be exact start of one of renames in the
110 // chain of renames (test-annotate2 repository, file1->file1a->file1b, i.e. points
111 // to the very start of file1a or file1 history), presence of == would get us to the next
112 // chunk and hence changed parents of present chunk's first element. Our annotate alg
113 // relies on parents only (i.e. knows nothing about 'last iteration element') to find out
114 // what to compare, and hence won't report all lines of 'last iteration element' (which is the
115 // first revision of the renamed file) as "added in this revision", leaving gaps in annotate
106 HgRepository repo = currentFile.getRepo(); 116 HgRepository repo = currentFile.getRepo();
107 Nodeid originLastRev = currentFile.getCopySourceRevision(); 117 Nodeid originLastRev = currentFile.getCopySourceRevision();
108 currentFile = repo.getFileNode(currentFile.getCopySourceName()); 118 currentFile = repo.getFileNode(currentFile.getCopySourceName());
109 fileLastClogRevIndex = currentFile.getChangesetRevisionIndex(currentFile.getRevisionIndex(originLastRev)); 119 fileLastClogRevIndex = currentFile.getChangesetRevisionIndex(currentFile.getRevisionIndex(originLastRev));
110 // XXX perhaps, shall fail with meaningful exception if new file doesn't exist (.i/.d not found for whatever reason) 120 // XXX perhaps, shall fail with meaningful exception if new file doesn't exist (.i/.d not found for whatever reason)
111 // or source revision is missing? 121 // or source revision is missing?
112 } else { 122 } else {
123 fileHistory.chopAtChangeset(changelogRevIndexStart);
113 currentFile = null; // stop iterating 124 currentFile = null; // stop iterating
114 } 125 }
115 } while (currentFile != null && fileLastClogRevIndex >= changelogRevIndexStart); 126 } while (currentFile != null && fileLastClogRevIndex > changelogRevIndexStart);
116 // fileCompleteHistory is in (origin, intermediate target, ultimate target) order 127 // fileCompleteHistory is in (origin, intermediate target, ultimate target) order
117 128
118 int[] fileClogParentRevs = new int[2]; 129 int[] fileClogParentRevs = new int[2];
119 int[] fileParentRevs = new int[2]; 130 int[] fileParentRevs = new int[2];
120 if (iterateOrder == NewToOld) { 131 if (iterateOrder == NewToOld) {
404 return; 415 return;
405 } 416 }
406 target.originFileRev = fileRevsToVisit.get(0); // files to visit are new to old 417 target.originFileRev = fileRevsToVisit.get(0); // files to visit are new to old
407 target.originChangelogRev = changeset(target.originFileRev); 418 target.originChangelogRev = changeset(target.originFileRev);
408 } 419 }
409 420
421 /**
422 * Mark revision closest(ceil) to specified as the very first one (no parents)
423 */
424 public void chopAtChangeset(int firstChangelogRevOfInterest) {
425 if (firstChangelogRevOfInterest == 0) {
426 return; // nothing to do
427 }
428 int i = 0, x = fileRevsToVisit.size(), fileRev = BAD_REVISION;
429 // fileRevsToVisit is new to old, greater numbers to smaller
430 while (i < x && changeset(fileRev = fileRevsToVisit.get(i)) >= firstChangelogRevOfInterest) {
431 i++;
432 }
433 assert fileRev != BAD_REVISION; // there's at least 1 revision in fileRevsToVisit
434 if (i == x && changeset(fileRev) != firstChangelogRevOfInterest) {
435 assert false : "Requested changeset shall belong to the chunk";
436 return;
437 }
438 fileRevsToVisit.trimTo(i); // no need to iterate more
439 // pretend fileRev got no parents
440 fileParentRevs.set(fileRev * 2, NO_REVISION);
441 fileParentRevs.set(fileRev, NO_REVISION);
442 }
443
410 public int[] fileRevisions(HgIterateDirection iterateOrder) { 444 public int[] fileRevisions(HgIterateDirection iterateOrder) {
411 // fileRevsToVisit is { r10, r7, r6, r5, r0 }, new to old 445 // fileRevsToVisit is { r10, r7, r6, r5, r0 }, new to old
412 int[] rv = fileRevsToVisit.toArray(); 446 int[] rv = fileRevsToVisit.toArray();
413 if (iterateOrder == OldToNew) { 447 if (iterateOrder == OldToNew) {
414 // reverse return value 448 // reverse return value