Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/FileHistory.java @ 691:72fc7774b87e
Fix file.isCopy() for blame/annotate. Refactor status and blame to use newly introduced FileHistory helper that builds file rename history
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Fri, 02 Aug 2013 23:07:23 +0200 |
| parents | 6526d8adbc0f |
| children |
comparison
equal
deleted
inserted
replaced
| 690:b286222158be | 691:72fc7774b87e |
|---|---|
| 15 * contact TMate Software at support@hg4j.com | 15 * contact TMate Software at support@hg4j.com |
| 16 */ | 16 */ |
| 17 package org.tmatesoft.hg.internal; | 17 package org.tmatesoft.hg.internal; |
| 18 | 18 |
| 19 import static org.tmatesoft.hg.core.HgIterateDirection.NewToOld; | 19 import static org.tmatesoft.hg.core.HgIterateDirection.NewToOld; |
| 20 import static org.tmatesoft.hg.core.HgIterateDirection.OldToNew; | |
| 20 | 21 |
| 21 import java.util.Collections; | 22 import java.util.Collections; |
| 22 import java.util.LinkedList; | 23 import java.util.LinkedList; |
| 23 | 24 |
| 24 import org.tmatesoft.hg.core.HgIterateDirection; | 25 import org.tmatesoft.hg.core.HgIterateDirection; |
| 25 import org.tmatesoft.hg.core.Nodeid; | 26 import org.tmatesoft.hg.core.Nodeid; |
| 27 import org.tmatesoft.hg.internal.FileRenameHistory.Chunk; | |
| 26 import org.tmatesoft.hg.repo.HgDataFile; | 28 import org.tmatesoft.hg.repo.HgDataFile; |
| 27 import org.tmatesoft.hg.repo.HgRepository; | |
| 28 import org.tmatesoft.hg.repo.HgRuntimeException; | 29 import org.tmatesoft.hg.repo.HgRuntimeException; |
| 29 | 30 |
| 30 /** | 31 /** |
| 31 * History of a file, with copy/renames, and corresponding revision information. | 32 * History of a file, with copy/renames, and corresponding revision information. |
| 32 * Facility for file history iteration. | 33 * Facility for file history iteration. |
| 56 public int getEndChangeset() { | 57 public int getEndChangeset() { |
| 57 return csetTo; | 58 return csetTo; |
| 58 } | 59 } |
| 59 | 60 |
| 60 public void build() throws HgRuntimeException { | 61 public void build() throws HgRuntimeException { |
| 61 assert fileCompleteHistory.isEmpty(); | |
| 62 HgDataFile currentFile = df; | |
| 63 final int changelogRevIndexEnd = csetTo; | |
| 64 final int changelogRevIndexStart = csetFrom; | |
| 65 int fileLastClogRevIndex = changelogRevIndexEnd; | |
| 66 FileRevisionHistoryChunk nextChunk = null; | |
| 67 fileCompleteHistory.clear(); // just in case, #build() is not expected to be called more than once | 62 fileCompleteHistory.clear(); // just in case, #build() is not expected to be called more than once |
| 68 do { | 63 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(csetTo, df.getPath()); |
| 69 FileRevisionHistoryChunk fileHistory = new FileRevisionHistoryChunk(currentFile); | 64 int fileRevIndex = df.getRevisionIndex(fileRev); |
| 70 fileHistory.init(fileLastClogRevIndex); | 65 FileRenameHistory frh = new FileRenameHistory(csetFrom, csetTo); |
| 71 fileHistory.linkTo(nextChunk); | 66 if (frh.isOutOfRange(df, fileRevIndex)) { |
| 72 fileCompleteHistory.addFirst(fileHistory); // to get the list in old-to-new order | 67 return; |
| 73 nextChunk = fileHistory; | 68 } |
| 74 if (fileHistory.changeset(0) > changelogRevIndexStart && currentFile.isCopy()) { | 69 frh.build(df, fileRevIndex); |
| 75 // fileHistory.changeset(0) is the earliest revision we know about so far, | 70 FileRevisionHistoryChunk prevChunk = null; |
| 76 // once we get to revisions earlier than the requested start, stop digging. | 71 for (Chunk c : frh.iterate(OldToNew)) { |
| 77 // The reason there's NO == (i.e. not >=) because: | 72 FileRevisionHistoryChunk fileHistory = new FileRevisionHistoryChunk(c.file(), c.firstCset(), c.lastCset(), c.firstFileRev(), c.lastFileRev()); |
| 78 // (easy): once it's equal, we've reached our intended start | 73 fileHistory.init(); |
| 79 // (hard): if changelogRevIndexStart happens to be exact start of one of renames in the | 74 if (fileHistory.revisionCount() == 0) { |
| 80 // chain of renames (test-annotate2 repository, file1->file1a->file1b, i.e. points | 75 // no revisions on our cset range of interest |
| 81 // to the very start of file1a or file1 history), presence of == would get us to the next | 76 continue; |
| 82 // chunk and hence changed parents of present chunk's first element. Our annotate alg | |
| 83 // relies on parents only (i.e. knows nothing about 'last iteration element') to find out | |
| 84 // what to compare, and hence won't report all lines of 'last iteration element' (which is the | |
| 85 // first revision of the renamed file) as "added in this revision", leaving gaps in annotate | |
| 86 HgRepository repo = currentFile.getRepo(); | |
| 87 Nodeid originLastRev = currentFile.getCopySourceRevision(); | |
| 88 currentFile = repo.getFileNode(currentFile.getCopySourceName()); | |
| 89 fileLastClogRevIndex = currentFile.getChangesetRevisionIndex(currentFile.getRevisionIndex(originLastRev)); | |
| 90 // XXX perhaps, shall fail with meaningful exception if new file doesn't exist (.i/.d not found for whatever reason) | |
| 91 // or source revision is missing? | |
| 92 } else { | |
| 93 fileHistory.chopAtChangeset(changelogRevIndexStart); | |
| 94 currentFile = null; // stop iterating | |
| 95 } | 77 } |
| 96 } while (currentFile != null && fileLastClogRevIndex > changelogRevIndexStart); | 78 if (prevChunk != null) { |
| 79 prevChunk.linkTo(fileHistory); | |
| 80 } | |
| 81 fileCompleteHistory.addLast(fileHistory); // to get the list in old-to-new order | |
| 82 prevChunk = fileHistory; | |
| 83 } | |
| 97 // fileCompleteHistory is in (origin, intermediate target, ultimate target) order | 84 // fileCompleteHistory is in (origin, intermediate target, ultimate target) order |
| 98 } | 85 } |
| 99 | 86 |
| 100 public Iterable<FileRevisionHistoryChunk> iterate(HgIterateDirection order) { | 87 public Iterable<FileRevisionHistoryChunk> iterate(HgIterateDirection order) { |
| 101 if (order == NewToOld) { | 88 if (order == NewToOld) { |
