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) { |